From 210762268466634ddbfaddb48fdf5181ce4b5f2d Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Sat, 27 Aug 2011 18:53:03 +0200 Subject: firewire: move fw_device reference counting from drivers to core fw_unit device drivers invariably need to talk to the fw_unit's parent (an fw_device) and grandparent (an fw_card). firewire-core already maintains an fw_card reference for the entire lifetime of an fw_device. Likewise, let firewire-core maintain an fw_device reference for the entire lifetime of an fw_unit so that fw_unit drivers don't have to. Signed-off-by: Stefan Richter --- include/linux/firewire.h | 12 ------------ 1 file changed, 12 deletions(-) (limited to 'include/linux') diff --git a/include/linux/firewire.h b/include/linux/firewire.h index 84ccf8e04fa6..6f1d7385e051 100644 --- a/include/linux/firewire.h +++ b/include/linux/firewire.h @@ -203,18 +203,6 @@ static inline int fw_device_is_shutdown(struct fw_device *device) return atomic_read(&device->state) == FW_DEVICE_SHUTDOWN; } -static inline struct fw_device *fw_device_get(struct fw_device *device) -{ - get_device(&device->device); - - return device; -} - -static inline void fw_device_put(struct fw_device *device) -{ - put_device(&device->device); -} - int fw_device_enable_phys_dma(struct fw_device *device); /* -- cgit v1.2.3 From 4d1a3a0dc551cfa7304ca46e014231500f3b81a6 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Tue, 13 Dec 2011 16:57:07 +0100 Subject: ARM: 7218/1: mmc: mmci: Provide option to configure bus signal direction The ST Micro variant supports bus signal direction indication. A new member in the variant struct is added for this. Moreover the actual signal direction configuration is board specific, thus the amba mmci platform data is extended with a new member to be able provide mmci with these specific board configurations. This patch is based upon a patch from Sebastian Rasmussen. Tested-by: Linus Walleij Signed-off-by: Sebastian Rasmussen Signed-off-by: Ulf Hansson Signed-off-by: Russell King --- drivers/mmc/host/mmci.c | 21 +++++++++++++++++++++ drivers/mmc/host/mmci.h | 10 ---------- include/linux/amba/mmci.h | 16 ++++++++++++++++ 3 files changed, 37 insertions(+), 10 deletions(-) (limited to 'include/linux') diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 7cc89beee87f..eb11ce61941d 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -54,6 +54,7 @@ static unsigned int fmax = 515633; * @st_clkdiv: true if using a ST-specific clock divider algorithm * @blksz_datactrl16: true if Block size is at b16..b30 position in datactrl register * @pwrreg_powerup: power up value for MMCIPOWER register + * @signal_direction: input/out direction of bus signals can be indicated */ struct variant_data { unsigned int clkreg; @@ -65,6 +66,7 @@ struct variant_data { bool st_clkdiv; bool blksz_datactrl16; u32 pwrreg_powerup; + bool signal_direction; }; static struct variant_data variant_arm = { @@ -88,6 +90,7 @@ static struct variant_data variant_u300 = { .datalength_bits = 16, .sdio = true, .pwrreg_powerup = MCI_PWR_ON, + .signal_direction = true, }; static struct variant_data variant_ux500 = { @@ -99,6 +102,7 @@ static struct variant_data variant_ux500 = { .sdio = true, .st_clkdiv = true, .pwrreg_powerup = MCI_PWR_ON, + .signal_direction = true, }; static struct variant_data variant_ux500v2 = { @@ -111,6 +115,7 @@ static struct variant_data variant_ux500v2 = { .st_clkdiv = true, .blksz_datactrl16 = true, .pwrreg_powerup = MCI_PWR_ON, + .signal_direction = true, }; /* @@ -1057,6 +1062,22 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) break; } + if (variant->signal_direction && ios->power_mode != MMC_POWER_OFF) { + /* + * The ST Micro variant has some additional bits + * indicating signal direction for the signals in + * the SD/MMC bus and feedback-clock usage. + */ + pwr |= host->plat->sigdir; + + if (ios->bus_width == MMC_BUS_WIDTH_4) + pwr &= ~MCI_ST_DATA74DIREN; + else if (ios->bus_width == MMC_BUS_WIDTH_1) + pwr &= (~MCI_ST_DATA74DIREN & + ~MCI_ST_DATA31DIREN & + ~MCI_ST_DATA2DIREN); + } + if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN) { if (host->hw_designer != AMBA_VENDOR_ST) pwr |= MCI_ROD; diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h index 49f153e6ef7a..89eb2e3556d3 100644 --- a/drivers/mmc/host/mmci.h +++ b/drivers/mmc/host/mmci.h @@ -13,16 +13,6 @@ #define MCI_PWR_ON 0x03 #define MCI_OD (1 << 6) #define MCI_ROD (1 << 7) -/* - * The ST Micro version does not have ROD and reuse the voltage registers - * for direction settings - */ -#define MCI_ST_DATA2DIREN (1 << 2) -#define MCI_ST_CMDDIREN (1 << 3) -#define MCI_ST_DATA0DIREN (1 << 4) -#define MCI_ST_DATA31DIREN (1 << 5) -#define MCI_ST_FBCLKEN (1 << 7) -#define MCI_ST_DATA74DIREN (1 << 8) #define MMCICLOCK 0x004 #define MCI_CLK_ENABLE (1 << 8) diff --git a/include/linux/amba/mmci.h b/include/linux/amba/mmci.h index 0101e9c17fa1..b51bf5fa85f8 100644 --- a/include/linux/amba/mmci.h +++ b/include/linux/amba/mmci.h @@ -6,6 +6,19 @@ #include + +/* + * These defines is places here due to access is needed from machine + * configuration files. The ST Micro version does not have ROD and + * reuse the voltage registers for direction settings. + */ +#define MCI_ST_DATA2DIREN (1 << 2) +#define MCI_ST_CMDDIREN (1 << 3) +#define MCI_ST_DATA0DIREN (1 << 4) +#define MCI_ST_DATA31DIREN (1 << 5) +#define MCI_ST_FBCLKEN (1 << 7) +#define MCI_ST_DATA74DIREN (1 << 8) + /* Just some dummy forwarding */ struct dma_chan; @@ -31,6 +44,8 @@ struct dma_chan; * @capabilities: the capabilities of the block as implemented in * this platform, signify anything MMC_CAP_* from mmc/host.h * @capabilities2: more capabilities, MMC_CAP2_* from mmc/host.h + * @sigdir: a bit field indicating for what bits in the MMC bus the host + * should enable signal direction indication. * @dma_filter: function used to select an appropriate RX and TX * DMA channel to be used for DMA, if and only if you're deploying the * generic DMA engine @@ -54,6 +69,7 @@ struct mmci_platform_data { bool cd_invert; unsigned long capabilities; unsigned long capabilities2; + u32 sigdir; bool (*dma_filter)(struct dma_chan *chan, void *filter_param); void *dma_rx_param; void *dma_tx_param; -- cgit v1.2.3 From bc521818e28042bb6018d91c353d24fb01ccb162 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Tue, 13 Dec 2011 16:57:55 +0100 Subject: ARM: 7219/1: mmc: mmci: Change vdd_handler to a generic ios_handler The purpose of the vdd_handler does not make sense. We remove it and use a generic approach instead. A new ios_handler is added, the purpose of which e.g. can be to control GPIO pins to a levelshifter. Previously the vdd_handler was also used for making additional changes to the power register bits. This option is superfluous and is therefore removed. Adaptaptions from the old vdd_handler to the new ios_handler is done for mach-ux500 board, which was the only one using the vdd_handler. This patch is based upon a patch from Sebastian Rasmussen. Tested-by: Linus Walleij Signed-off-by: Sebastian Rasmussen Signed-off-by: Ulf Hansson Signed-off-by: Russell King --- arch/arm/mach-ux500/board-mop500-sdi.c | 21 ++++++++------------- drivers/mmc/host/mmci.c | 8 ++++---- include/linux/amba/mmci.h | 6 +++--- 3 files changed, 15 insertions(+), 20 deletions(-) (limited to 'include/linux') diff --git a/arch/arm/mach-ux500/board-mop500-sdi.c b/arch/arm/mach-ux500/board-mop500-sdi.c index 23be34b3bb6e..4049bd7f061f 100644 --- a/arch/arm/mach-ux500/board-mop500-sdi.c +++ b/arch/arm/mach-ux500/board-mop500-sdi.c @@ -31,21 +31,13 @@ * SDI 0 (MicroSD slot) */ -/* MMCIPOWER bits */ -#define MCI_DATA2DIREN (1 << 2) -#define MCI_CMDDIREN (1 << 3) -#define MCI_DATA0DIREN (1 << 4) -#define MCI_DATA31DIREN (1 << 5) -#define MCI_FBCLKEN (1 << 7) - /* GPIO pins used by the sdi0 level shifter */ static int sdi0_en = -1; static int sdi0_vsel = -1; -static u32 mop500_sdi0_vdd_handler(struct device *dev, unsigned int vdd, - unsigned char power_mode) +static int mop500_sdi0_ios_handler(struct device *dev, struct mmc_ios *ios) { - switch (power_mode) { + switch (ios->power_mode) { case MMC_POWER_UP: case MMC_POWER_ON: /* @@ -65,8 +57,7 @@ static u32 mop500_sdi0_vdd_handler(struct device *dev, unsigned int vdd, break; } - return MCI_FBCLKEN | MCI_CMDDIREN | MCI_DATA0DIREN | - MCI_DATA2DIREN | MCI_DATA31DIREN; + return 0; } #ifdef CONFIG_STE_DMA40 @@ -90,13 +81,17 @@ static struct stedma40_chan_cfg mop500_sdi0_dma_cfg_tx = { #endif static struct mmci_platform_data mop500_sdi0_data = { - .vdd_handler = mop500_sdi0_vdd_handler, + .ios_handler = mop500_sdi0_ios_handler, .ocr_mask = MMC_VDD_29_30, .f_max = 50000000, .capabilities = MMC_CAP_4_BIT_DATA | MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED, .gpio_wp = -1, + .sigdir = MCI_ST_FBCLKEN | + MCI_ST_CMDDIREN | + MCI_ST_DATA0DIREN | + MCI_ST_DATA2DIREN, #ifdef CONFIG_STE_DMA40 .dma_filter = stedma40_filter, .dma_rx_param = &mop500_sdi0_dma_cfg_rx, diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index eb11ce61941d..0af1507d15c0 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -1026,6 +1026,10 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) unsigned long flags; int ret; + if (host->plat->ios_handler && + host->plat->ios_handler(mmc_dev(mmc), ios)) + dev_err(mmc_dev(mmc), "platform ios_handler failed\n"); + switch (ios->power_mode) { case MMC_POWER_OFF: if (host->vcc) @@ -1045,10 +1049,6 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) return; } } - if (host->plat->vdd_handler) - pwr |= host->plat->vdd_handler(mmc_dev(mmc), ios->vdd, - ios->power_mode); - /* * The ST Micro variant doesn't have the PL180s MCI_PWR_UP * and instead uses MCI_PWR_ON so apply whatever value is diff --git a/include/linux/amba/mmci.h b/include/linux/amba/mmci.h index b51bf5fa85f8..32a89cf5ec45 100644 --- a/include/linux/amba/mmci.h +++ b/include/linux/amba/mmci.h @@ -31,7 +31,8 @@ struct dma_chan; * @ocr_mask: available voltages on the 4 pins from the block, this * is ignored if a regulator is used, see the MMC_VDD_* masks in * mmc/host.h - * @vdd_handler: a callback function to translate a MMC_VDD_* + * @ios_handler: a callback function to act on specfic ios changes, + * used for example to control a levelshifter * mask into a value to be binary (or set some other custom bits * in MMCIPWR) or:ed and written into the MMCIPWR register of the * block. May also control external power based on the power_mode. @@ -61,8 +62,7 @@ struct dma_chan; struct mmci_platform_data { unsigned int f_max; unsigned int ocr_mask; - u32 (*vdd_handler)(struct device *, unsigned int vdd, - unsigned char power_mode); + int (*ios_handler)(struct device *, struct mmc_ios *); unsigned int (*status)(struct device *); int gpio_wp; int gpio_cd; -- cgit v1.2.3 From 82159ba8e6ef8c38e3e0452d90b4ff8da9e4b2c1 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 18 Jan 2012 10:52:25 +0000 Subject: regmap: Add support for padding between register and address Some devices, especially those with high speed control interfaces, require padding between the register and the data. Support this in the regmap API by providing a pad_bits configuration parameter. Only devices with integer byte counts are supported. Signed-off-by: Mark Brown --- drivers/base/regmap/internal.h | 1 + drivers/base/regmap/regmap.c | 32 +++++++++++++++++++++----------- include/linux/regmap.h | 2 ++ 3 files changed, 24 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 1a02b7537c8b..e33f1be2b299 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -22,6 +22,7 @@ struct regcache_ops; struct regmap_format { size_t buf_size; size_t reg_bytes; + size_t pad_bytes; size_t val_bytes; void (*format_write)(struct regmap *map, unsigned int reg, unsigned int val); diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index be10a4ff6609..e9c2ac0174c6 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -160,7 +160,9 @@ struct regmap *regmap_init(struct device *dev, mutex_init(&map->lock); map->format.buf_size = (config->reg_bits + config->val_bits) / 8; map->format.reg_bytes = config->reg_bits / 8; + map->format.pad_bytes = config->pad_bits / 8; map->format.val_bytes = config->val_bits / 8; + map->format.buf_size += map->format.pad_bytes; map->dev = dev; map->bus = bus; map->max_register = config->max_register; @@ -235,7 +237,7 @@ struct regmap *regmap_init(struct device *dev, !(map->format.format_reg && map->format.format_val)) goto err_map; - map->work_buf = kmalloc(map->format.buf_size, GFP_KERNEL); + map->work_buf = kzalloc(map->format.buf_size, GFP_KERNEL); if (map->work_buf == NULL) { ret = -ENOMEM; goto err_map; @@ -329,23 +331,28 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg, * send the work_buf directly, otherwise try to do a gather * write. */ - if (val == map->work_buf + map->format.reg_bytes) + if (val == (map->work_buf + map->format.pad_bytes + + map->format.reg_bytes)) ret = map->bus->write(map->dev, map->work_buf, - map->format.reg_bytes + val_len); + map->format.reg_bytes + + map->format.pad_bytes + + val_len); else if (map->bus->gather_write) ret = map->bus->gather_write(map->dev, map->work_buf, - map->format.reg_bytes, + map->format.reg_bytes + + map->format.pad_bytes, val, val_len); /* If that didn't work fall back on linearising by hand. */ if (ret == -ENOTSUPP) { - len = map->format.reg_bytes + val_len; - buf = kmalloc(len, GFP_KERNEL); + len = map->format.reg_bytes + map->format.pad_bytes + val_len; + buf = kzalloc(len, GFP_KERNEL); if (!buf) return -ENOMEM; memcpy(buf, map->work_buf, map->format.reg_bytes); - memcpy(buf + map->format.reg_bytes, val, val_len); + memcpy(buf + map->format.reg_bytes + map->format.pad_bytes, + val, val_len); ret = map->bus->write(map->dev, buf, len); kfree(buf); @@ -387,10 +394,12 @@ int _regmap_write(struct regmap *map, unsigned int reg, return ret; } else { - map->format.format_val(map->work_buf + map->format.reg_bytes, - val); + map->format.format_val(map->work_buf + map->format.reg_bytes + + map->format.pad_bytes, val); return _regmap_raw_write(map, reg, - map->work_buf + map->format.reg_bytes, + map->work_buf + + map->format.reg_bytes + + map->format.pad_bytes, map->format.val_bytes); } } @@ -473,7 +482,8 @@ static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val, trace_regmap_hw_read_start(map->dev, reg, val_len / map->format.val_bytes); - ret = map->bus->read(map->dev, map->work_buf, map->format.reg_bytes, + ret = map->bus->read(map->dev, map->work_buf, + map->format.reg_bytes + map->format.pad_bytes, val, val_len); trace_regmap_hw_read_done(map->dev, reg, diff --git a/include/linux/regmap.h b/include/linux/regmap.h index eb93921cdd30..a6ed6e6e27ac 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -44,6 +44,7 @@ struct reg_default { * Configuration for the register map of a device. * * @reg_bits: Number of bits in a register address, mandatory. + * @pad_bits: Number of bits of padding between register and value. * @val_bits: Number of bits in a register value, mandatory. * * @writeable_reg: Optional callback returning true if the register @@ -74,6 +75,7 @@ struct reg_default { */ struct regmap_config { int reg_bits; + int pad_bits; int val_bits; bool (*writeable_reg)(struct device *dev, unsigned int reg); -- cgit v1.2.3 From 583aa3a9b5ca846a84f7dd87bdc4b75dca07b011 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 11 Jan 2012 06:45:05 -0300 Subject: [media] V4L2: Add per-device-node capabilities If V4L2_CAP_DEVICE_CAPS is set, then the new device_caps field is filled with the capabilities of the opened device node. The capabilities field traditionally contains the capabilities of the physical device, being a superset of all capabilities available at the several device nodes. E.g., if you open /dev/video0, then if it contains VBI caps then that means that there is a corresponding vbi node as well. And the capabilities field of both the video and vbi nodes should contain identical caps. However, it would be very useful to also have a capabilities field that contains just the caps for the currently open device, hence the new CAP bit and field. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/compat.xml | 4 +++ Documentation/DocBook/media/v4l/v4l2.xml | 9 +++++- .../DocBook/media/v4l/vidioc-querycap.xml | 36 ++++++++++++++++++++-- drivers/media/video/cx231xx/cx231xx-417.c | 1 - drivers/media/video/pvrusb2/pvrusb2-v4l2.c | 1 - drivers/media/video/v4l2-ioctl.c | 6 ++-- include/linux/videodev2.h | 29 +++++++++++------ 7 files changed, 69 insertions(+), 17 deletions(-) (limited to 'include/linux') diff --git a/Documentation/DocBook/media/v4l/compat.xml b/Documentation/DocBook/media/v4l/compat.xml index c736380b4647..c93298ff3279 100644 --- a/Documentation/DocBook/media/v4l/compat.xml +++ b/Documentation/DocBook/media/v4l/compat.xml @@ -2393,6 +2393,10 @@ details. to the User controls class. + + Added the device_caps field to struct v4l2_capabilities and added the new + V4L2_CAP_DEVICE_CAPS capability. + diff --git a/Documentation/DocBook/media/v4l/v4l2.xml b/Documentation/DocBook/media/v4l/v4l2.xml index e97c512861bb..dce3fef15bc9 100644 --- a/Documentation/DocBook/media/v4l/v4l2.xml +++ b/Documentation/DocBook/media/v4l/v4l2.xml @@ -127,6 +127,13 @@ structs, ioctls) must be noted in more detail in the history chapter (compat.xml), along with the possible impact on existing drivers and applications. --> + + 3.3 + 2012-01-11 + hv + Added device_caps field to struct v4l2_capabilities. + + 3.2 2011-08-26 @@ -417,7 +424,7 @@ and discussions on the V4L mailing list. Video for Linux Two API Specification - Revision 3.2 + Revision 3.3 &sub-common; diff --git a/Documentation/DocBook/media/v4l/vidioc-querycap.xml b/Documentation/DocBook/media/v4l/vidioc-querycap.xml index e3664d6f2de4..4643505cd4ca 100644 --- a/Documentation/DocBook/media/v4l/vidioc-querycap.xml +++ b/Documentation/DocBook/media/v4l/vidioc-querycap.xml @@ -124,12 +124,35 @@ printf ("Version: %u.%u.%u\n", __u32 capabilities - Device capabilities, see . + Available capabilities of the physical device as a whole, see . The same physical device can export + multiple devices in /dev (e.g. /dev/videoX, /dev/vbiY and /dev/radioZ). + The capabilities field should contain a union + of all capabilities available around the several V4L2 devices exported + to userspace. + For all those devices the capabilities field + returns the same set of capabilities. This allows applications to open + just one of the devices (typically the video device) and discover whether + video, vbi and/or radio are also supported. + __u32 - reserved[4] + device_caps + Device capabilities of the opened device, see . Should contain the available capabilities + of that specific device node. So, for example, device_caps + of a radio device will only contain radio related capabilities and + no video or vbi capabilities. This field is only set if the capabilities + field contains the V4L2_CAP_DEVICE_CAPS capability. + Only the capabilities field can have the + V4L2_CAP_DEVICE_CAPS capability, device_caps + will never set V4L2_CAP_DEVICE_CAPS. + + + + __u32 + reserved[3] Reserved for future extensions. Drivers must set this array to zero. @@ -276,6 +299,13 @@ linkend="async">asynchronous I/O methods. The device supports the streaming I/O method. + + V4L2_CAP_DEVICE_CAPS + 0x80000000 + The driver fills the device_caps + field. This capability can only appear in the capabilities + field and never in the device_caps field. + diff --git a/drivers/media/video/cx231xx/cx231xx-417.c b/drivers/media/video/cx231xx/cx231xx-417.c index f8f0e59cd583..d4327dab5a36 100644 --- a/drivers/media/video/cx231xx/cx231xx-417.c +++ b/drivers/media/video/cx231xx/cx231xx-417.c @@ -1686,7 +1686,6 @@ static struct v4l2_capability pvr_capability = { .capabilities = (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER | V4L2_CAP_AUDIO | V4L2_CAP_RADIO | V4L2_CAP_STREAMING | V4L2_CAP_READWRITE), - .reserved = {0, 0, 0, 0} }; static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) diff --git a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c index 6d666174dbb4..e1111d968a3d 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c +++ b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c @@ -96,7 +96,6 @@ static struct v4l2_capability pvr_capability ={ .capabilities = (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER | V4L2_CAP_AUDIO | V4L2_CAP_RADIO | V4L2_CAP_READWRITE), - .reserved = {0,0,0,0} }; static struct v4l2_fmtdesc pvr_fmtdesc [] = { diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c index 3f623859a337..d0d7281e01e0 100644 --- a/drivers/media/video/v4l2-ioctl.c +++ b/drivers/media/video/v4l2-ioctl.c @@ -540,10 +540,12 @@ static long __video_do_ioctl(struct file *file, if (!ret) dbgarg(cmd, "driver=%s, card=%s, bus=%s, " "version=0x%08x, " - "capabilities=0x%08x\n", + "capabilities=0x%08x, " + "device_caps=0x%08x\n", cap->driver, cap->card, cap->bus_info, cap->version, - cap->capabilities); + cap->capabilities, + cap->device_caps); break; } diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index 5e11f8a1f867..0db05033c2ec 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -235,16 +235,25 @@ struct v4l2_fract { __u32 denominator; }; -/* - * D R I V E R C A P A B I L I T I E S - */ +/** + * struct v4l2_capability - Describes V4L2 device caps returned by VIDIOC_QUERYCAP + * + * @driver: name of the driver module (e.g. "bttv") + * @card: name of the card (e.g. "Hauppauge WinTV") + * @bus_info: name of the bus (e.g. "PCI:" + pci_name(pci_dev) ) + * @version: KERNEL_VERSION + * @capabilities: capabilities of the physical device as a whole + * @device_caps: capabilities accessed via this particular device (node) + * @reserved: reserved fields for future extensions + */ struct v4l2_capability { - __u8 driver[16]; /* i.e. "bttv" */ - __u8 card[32]; /* i.e. "Hauppauge WinTV" */ - __u8 bus_info[32]; /* "PCI:" + pci_name(pci_dev) */ - __u32 version; /* should use KERNEL_VERSION() */ - __u32 capabilities; /* Device capabilities */ - __u32 reserved[4]; + __u8 driver[16]; + __u8 card[32]; + __u8 bus_info[32]; + __u32 version; + __u32 capabilities; + __u32 device_caps; + __u32 reserved[3]; }; /* Values for 'capabilities' field */ @@ -274,6 +283,8 @@ struct v4l2_capability { #define V4L2_CAP_ASYNCIO 0x02000000 /* async I/O */ #define V4L2_CAP_STREAMING 0x04000000 /* streaming I/O ioctls */ +#define V4L2_CAP_DEVICE_CAPS 0x80000000 /* sets device capabilities field */ + /* * V I D E O I M A G E F O R M A T */ -- cgit v1.2.3 From 0ce8974d504913a0f0ae2d97b20a5ac665431a41 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Fri, 6 Jan 2012 03:13:27 -0800 Subject: sysctl: Consolidate !CONFIG_SYSCTL handling - In sysctl.h move functions only available if CONFIG_SYSCL is defined inside of #ifdef CONFIG_SYSCTL - Move the stub function definitions for !CONFIG_SYSCTL into sysctl.h and make them static inlines. Signed-off-by: Eric W. Biederman --- include/linux/sysctl.h | 95 ++++++++++++++++++++++++++++++++------------------ kernel/sysctl.c | 26 -------------- 2 files changed, 62 insertions(+), 59 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index bb9127dd814b..cf3ee7f246d6 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -937,30 +937,8 @@ enum struct ctl_table; struct nsproxy; struct ctl_table_root; - -struct ctl_table_set { - struct list_head list; - struct ctl_table_set *parent; - int (*is_seen)(struct ctl_table_set *); -}; - -extern void setup_sysctl_set(struct ctl_table_set *p, - struct ctl_table_set *parent, - int (*is_seen)(struct ctl_table_set *)); - struct ctl_table_header; -extern void sysctl_head_get(struct ctl_table_header *); -extern void sysctl_head_put(struct ctl_table_header *); -extern int sysctl_is_seen(struct ctl_table_header *); -extern struct ctl_table_header *sysctl_head_grab(struct ctl_table_header *); -extern struct ctl_table_header *sysctl_head_next(struct ctl_table_header *prev); -extern struct ctl_table_header *__sysctl_head_next(struct nsproxy *namespaces, - struct ctl_table_header *prev); -extern void sysctl_head_finish(struct ctl_table_header *prev); -extern int sysctl_perm(struct ctl_table_root *root, - struct ctl_table *table, int op); - typedef struct ctl_table ctl_table; typedef int proc_handler (struct ctl_table *ctl, int write, @@ -1023,8 +1001,6 @@ static inline void *proc_sys_poll_event(struct ctl_table_poll *poll) return (void *)(unsigned long)atomic_read(&poll->event); } -void proc_sys_poll_notify(struct ctl_table_poll *poll); - #define __CTL_TABLE_POLL_INITIALIZER(name) { \ .event = ATOMIC_INIT(0), \ .wait = __WAIT_QUEUE_HEAD_INITIALIZER(name.wait) } @@ -1047,15 +1023,6 @@ struct ctl_table void *extra2; }; -struct ctl_table_root { - struct list_head root_list; - struct ctl_table_set default_set; - struct ctl_table_set *(*lookup)(struct ctl_table_root *root, - struct nsproxy *namespaces); - int (*permissions)(struct ctl_table_root *root, - struct nsproxy *namespaces, struct ctl_table *table); -}; - /* struct ctl_table_header is used to maintain dynamic lists of struct ctl_table trees. */ struct ctl_table_header @@ -1078,11 +1045,45 @@ struct ctl_table_header struct ctl_table_header *parent; }; +struct ctl_table_set { + struct list_head list; + struct ctl_table_set *parent; + int (*is_seen)(struct ctl_table_set *); +}; + +struct ctl_table_root { + struct list_head root_list; + struct ctl_table_set default_set; + struct ctl_table_set *(*lookup)(struct ctl_table_root *root, + struct nsproxy *namespaces); + int (*permissions)(struct ctl_table_root *root, + struct nsproxy *namespaces, struct ctl_table *table); +}; + /* struct ctl_path describes where in the hierarchy a table is added */ struct ctl_path { const char *procname; }; +#ifdef CONFIG_SYSCTL + +void proc_sys_poll_notify(struct ctl_table_poll *poll); + +extern void setup_sysctl_set(struct ctl_table_set *p, + struct ctl_table_set *parent, + int (*is_seen)(struct ctl_table_set *)); + +extern void sysctl_head_get(struct ctl_table_header *); +extern void sysctl_head_put(struct ctl_table_header *); +extern int sysctl_is_seen(struct ctl_table_header *); +extern struct ctl_table_header *sysctl_head_grab(struct ctl_table_header *); +extern struct ctl_table_header *sysctl_head_next(struct ctl_table_header *prev); +extern struct ctl_table_header *__sysctl_head_next(struct nsproxy *namespaces, + struct ctl_table_header *prev); +extern void sysctl_head_finish(struct ctl_table_header *prev); +extern int sysctl_perm(struct ctl_table_root *root, + struct ctl_table *table, int op); + void register_sysctl_root(struct ctl_table_root *root); struct ctl_table_header *__register_sysctl_paths( struct ctl_table_root *root, struct nsproxy *namespaces, @@ -1094,6 +1095,34 @@ struct ctl_table_header *register_sysctl_paths(const struct ctl_path *path, void unregister_sysctl_table(struct ctl_table_header * table); int sysctl_check_table(struct nsproxy *namespaces, struct ctl_table *table); +#else /* CONFIG_SYSCTL */ +static inline struct ctl_table_header *register_sysctl_table(struct ctl_table * table) +{ + return NULL; +} + +static inline struct ctl_table_header *register_sysctl_paths( + const struct ctl_path *path, struct ctl_table *table) +{ + return NULL; +} + +static inline void unregister_sysctl_table(struct ctl_table_header * table) +{ +} + +static inline void setup_sysctl_set(struct ctl_table_set *p, + struct ctl_table_set *parent, + int (*is_seen)(struct ctl_table_set *)) +{ +} + +static inline void sysctl_head_put(struct ctl_table_header *head) +{ +} + +#endif /* CONFIG_SYSCTL */ + #endif /* __KERNEL__ */ #endif /* _LINUX_SYSCTL_H */ diff --git a/kernel/sysctl.c b/kernel/sysctl.c index f487f257e05e..d5bbddd0de24 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -2017,32 +2017,6 @@ void setup_sysctl_set(struct ctl_table_set *p, p->is_seen = is_seen; } -#else /* !CONFIG_SYSCTL */ -struct ctl_table_header *register_sysctl_table(struct ctl_table * table) -{ - return NULL; -} - -struct ctl_table_header *register_sysctl_paths(const struct ctl_path *path, - struct ctl_table *table) -{ - return NULL; -} - -void unregister_sysctl_table(struct ctl_table_header * table) -{ -} - -void setup_sysctl_set(struct ctl_table_set *p, - struct ctl_table_set *parent, - int (*is_seen)(struct ctl_table_set *)) -{ -} - -void sysctl_head_put(struct ctl_table_header *head) -{ -} - #endif /* CONFIG_SYSCTL */ /* -- cgit v1.2.3 From de4e83bd6b5e16d491ec068cd22801d5d063b07a Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Fri, 6 Jan 2012 03:34:20 -0800 Subject: sysctl: Register the base sysctl table like any other sysctl table. Simplify the code by treating the base sysctl table like any other sysctl table and register it with register_sysctl_table. To ensure this table is registered early enough to avoid problems call sysctl_init from proc_sys_init. Rename sysctl_net.c:sysctl_init() to net_sysctl_init() to avoid name conflicts now that kernel/sysctl.c:sysctl_init() is no longer static. Signed-off-by: Eric W. Biederman --- fs/proc/proc_sysctl.c | 3 ++- include/linux/sysctl.h | 1 + kernel/sysctl.c | 13 ++++--------- net/sysctl_net.c | 4 ++-- 4 files changed, 9 insertions(+), 12 deletions(-) (limited to 'include/linux') diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c index d82f4a8b4b80..9d29d28af577 100644 --- a/fs/proc/proc_sysctl.c +++ b/fs/proc/proc_sysctl.c @@ -468,5 +468,6 @@ int __init proc_sys_init(void) proc_sys_root->proc_iops = &proc_sys_dir_operations; proc_sys_root->proc_fops = &proc_sys_dir_file_operations; proc_sys_root->nlink = 0; - return 0; + + return sysctl_init(); } diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index cf3ee7f246d6..5e3532e9599f 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -1095,6 +1095,7 @@ struct ctl_table_header *register_sysctl_paths(const struct ctl_path *path, void unregister_sysctl_table(struct ctl_table_header * table); int sysctl_check_table(struct nsproxy *namespaces, struct ctl_table *table); +extern int sysctl_init(void); #else /* CONFIG_SYSCTL */ static inline struct ctl_table_header *register_sysctl_table(struct ctl_table * table) { diff --git a/kernel/sysctl.c b/kernel/sysctl.c index d5bbddd0de24..ad460248acc7 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -192,7 +192,7 @@ static int sysrq_sysctl_handler(ctl_table *table, int write, #endif -static struct ctl_table root_table[]; +static struct ctl_table root_table[1]; static struct ctl_table_root sysctl_table_root; static struct ctl_table_header root_table_header = { {{.count = 1, @@ -222,7 +222,7 @@ int sysctl_legacy_va_layout; /* The default sysctl tables: */ -static struct ctl_table root_table[] = { +static struct ctl_table sysctl_base_table[] = { { .procname = "kernel", .mode = 0555, @@ -1747,17 +1747,12 @@ static void sysctl_set_parent(struct ctl_table *parent, struct ctl_table *table) } } -static __init int sysctl_init(void) +int __init sysctl_init(void) { - sysctl_set_parent(NULL, root_table); -#ifdef CONFIG_SYSCTL_SYSCALL_CHECK - sysctl_check_table(current->nsproxy, root_table); -#endif + register_sysctl_table(sysctl_base_table); return 0; } -core_initcall(sysctl_init); - static struct ctl_table *is_branch_in(struct ctl_table *branch, struct ctl_table *table) { diff --git a/net/sysctl_net.c b/net/sysctl_net.c index e75813904f26..a6bbee2bc710 100644 --- a/net/sysctl_net.c +++ b/net/sysctl_net.c @@ -90,7 +90,7 @@ static struct pernet_operations sysctl_pernet_ops = { .exit = sysctl_net_exit, }; -static __init int sysctl_init(void) +static __init int net_sysctl_init(void) { int ret; ret = register_pernet_subsys(&sysctl_pernet_ops); @@ -102,7 +102,7 @@ static __init int sysctl_init(void) out: return ret; } -subsys_initcall(sysctl_init); +subsys_initcall(net_sysctl_init); struct ctl_table_header *register_net_sysctl_table(struct net *net, const struct ctl_path *path, struct ctl_table *table) -- cgit v1.2.3 From 1f87f0b52b1d6581168cb80f86746bc4df918d01 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Fri, 6 Jan 2012 04:07:15 -0800 Subject: sysctl: Move the implementation into fs/proc/proc_sysctl.c Move the core sysctl code from kernel/sysctl.c and kernel/sysctl_check.c into fs/proc/proc_sysctl.c. Currently sysctl maintenance is hampered by the sysctl implementation being split across 3 files with artificial layering between them. Consolidate the entire sysctl implementation into 1 file so that it is easier to see what is going on and hopefully allowing for simpler maintenance. For functions that are now only used in fs/proc/proc_sysctl.c remove their declarations from sysctl.h and make them static in fs/proc/proc_sysctl.c Signed-off-by: Eric W. Biederman --- fs/proc/internal.h | 3 + fs/proc/proc_sysctl.c | 622 +++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/sysctl.h | 16 -- kernel/Makefile | 1 - kernel/sysctl.c | 464 ------------------------------------ kernel/sysctl_check.c | 160 ------------- 6 files changed, 625 insertions(+), 641 deletions(-) delete mode 100644 kernel/sysctl_check.c (limited to 'include/linux') diff --git a/fs/proc/internal.h b/fs/proc/internal.h index 292577531ad1..3b5ecd960d6a 100644 --- a/fs/proc/internal.h +++ b/fs/proc/internal.h @@ -10,12 +10,15 @@ */ #include +struct ctl_table_header; extern struct proc_dir_entry proc_root; #ifdef CONFIG_PROC_SYSCTL extern int proc_sys_init(void); +extern void sysctl_head_put(struct ctl_table_header *head); #else static inline void proc_sys_init(void) { } +static inline void sysctl_head_put(struct ctl_table_header *head) { } #endif #ifdef CONFIG_NET extern int proc_net_init(void); diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c index 9d29d28af577..06e6f10ee8ec 100644 --- a/fs/proc/proc_sysctl.c +++ b/fs/proc/proc_sysctl.c @@ -7,6 +7,7 @@ #include #include #include +#include #include "internal.h" static const struct dentry_operations proc_sys_dentry_operations; @@ -24,6 +25,209 @@ void proc_sys_poll_notify(struct ctl_table_poll *poll) wake_up_interruptible(&poll->wait); } +static struct ctl_table root_table[1]; +static struct ctl_table_root sysctl_table_root; +static struct ctl_table_header root_table_header = { + {{.count = 1, + .ctl_table = root_table, + .ctl_entry = LIST_HEAD_INIT(sysctl_table_root.default_set.list),}}, + .root = &sysctl_table_root, + .set = &sysctl_table_root.default_set, +}; +static struct ctl_table_root sysctl_table_root = { + .root_list = LIST_HEAD_INIT(sysctl_table_root.root_list), + .default_set.list = LIST_HEAD_INIT(root_table_header.ctl_entry), +}; + +static DEFINE_SPINLOCK(sysctl_lock); + +/* called under sysctl_lock */ +static int use_table(struct ctl_table_header *p) +{ + if (unlikely(p->unregistering)) + return 0; + p->used++; + return 1; +} + +/* called under sysctl_lock */ +static void unuse_table(struct ctl_table_header *p) +{ + if (!--p->used) + if (unlikely(p->unregistering)) + complete(p->unregistering); +} + +/* called under sysctl_lock, will reacquire if has to wait */ +static void start_unregistering(struct ctl_table_header *p) +{ + /* + * if p->used is 0, nobody will ever touch that entry again; + * we'll eliminate all paths to it before dropping sysctl_lock + */ + if (unlikely(p->used)) { + struct completion wait; + init_completion(&wait); + p->unregistering = &wait; + spin_unlock(&sysctl_lock); + wait_for_completion(&wait); + spin_lock(&sysctl_lock); + } else { + /* anything non-NULL; we'll never dereference it */ + p->unregistering = ERR_PTR(-EINVAL); + } + /* + * do not remove from the list until nobody holds it; walking the + * list in do_sysctl() relies on that. + */ + list_del_init(&p->ctl_entry); +} + +static void sysctl_head_get(struct ctl_table_header *head) +{ + spin_lock(&sysctl_lock); + head->count++; + spin_unlock(&sysctl_lock); +} + +void sysctl_head_put(struct ctl_table_header *head) +{ + spin_lock(&sysctl_lock); + if (!--head->count) + kfree_rcu(head, rcu); + spin_unlock(&sysctl_lock); +} + +static struct ctl_table_header *sysctl_head_grab(struct ctl_table_header *head) +{ + if (!head) + BUG(); + spin_lock(&sysctl_lock); + if (!use_table(head)) + head = ERR_PTR(-ENOENT); + spin_unlock(&sysctl_lock); + return head; +} + +static void sysctl_head_finish(struct ctl_table_header *head) +{ + if (!head) + return; + spin_lock(&sysctl_lock); + unuse_table(head); + spin_unlock(&sysctl_lock); +} + +static struct ctl_table_set * +lookup_header_set(struct ctl_table_root *root, struct nsproxy *namespaces) +{ + struct ctl_table_set *set = &root->default_set; + if (root->lookup) + set = root->lookup(root, namespaces); + return set; +} + +static struct list_head * +lookup_header_list(struct ctl_table_root *root, struct nsproxy *namespaces) +{ + struct ctl_table_set *set = lookup_header_set(root, namespaces); + return &set->list; +} + +static struct ctl_table_header *__sysctl_head_next(struct nsproxy *namespaces, + struct ctl_table_header *prev) +{ + struct ctl_table_root *root; + struct list_head *header_list; + struct ctl_table_header *head; + struct list_head *tmp; + + spin_lock(&sysctl_lock); + if (prev) { + head = prev; + tmp = &prev->ctl_entry; + unuse_table(prev); + goto next; + } + tmp = &root_table_header.ctl_entry; + for (;;) { + head = list_entry(tmp, struct ctl_table_header, ctl_entry); + + if (!use_table(head)) + goto next; + spin_unlock(&sysctl_lock); + return head; + next: + root = head->root; + tmp = tmp->next; + header_list = lookup_header_list(root, namespaces); + if (tmp != header_list) + continue; + + do { + root = list_entry(root->root_list.next, + struct ctl_table_root, root_list); + if (root == &sysctl_table_root) + goto out; + header_list = lookup_header_list(root, namespaces); + } while (list_empty(header_list)); + tmp = header_list->next; + } +out: + spin_unlock(&sysctl_lock); + return NULL; +} + +static struct ctl_table_header *sysctl_head_next(struct ctl_table_header *prev) +{ + return __sysctl_head_next(current->nsproxy, prev); +} + +void register_sysctl_root(struct ctl_table_root *root) +{ + spin_lock(&sysctl_lock); + list_add_tail(&root->root_list, &sysctl_table_root.root_list); + spin_unlock(&sysctl_lock); +} + +/* + * sysctl_perm does NOT grant the superuser all rights automatically, because + * some sysctl variables are readonly even to root. + */ + +static int test_perm(int mode, int op) +{ + if (!current_euid()) + mode >>= 6; + else if (in_egroup_p(0)) + mode >>= 3; + if ((op & ~mode & (MAY_READ|MAY_WRITE|MAY_EXEC)) == 0) + return 0; + return -EACCES; +} + +static int sysctl_perm(struct ctl_table_root *root, struct ctl_table *table, int op) +{ + int mode; + + if (root->permissions) + mode = root->permissions(root, current->nsproxy, table); + else + mode = table->mode; + + return test_perm(mode, op); +} + +static void sysctl_set_parent(struct ctl_table *parent, struct ctl_table *table) +{ + for (; table->procname; table++) { + table->parent = parent; + if (table->child) + sysctl_set_parent(table, table->child); + } +} + + static struct inode *proc_sys_make_inode(struct super_block *sb, struct ctl_table_header *head, struct ctl_table *table) { @@ -435,6 +639,21 @@ static int proc_sys_delete(const struct dentry *dentry) return !!PROC_I(dentry->d_inode)->sysctl->unregistering; } +static int sysctl_is_seen(struct ctl_table_header *p) +{ + struct ctl_table_set *set = p->set; + int res; + spin_lock(&sysctl_lock); + if (p->unregistering) + res = 0; + else if (!set->is_seen) + res = 1; + else + res = set->is_seen(set); + spin_unlock(&sysctl_lock); + return res; +} + static int proc_sys_compare(const struct dentry *parent, const struct inode *pinode, const struct dentry *dentry, const struct inode *inode, @@ -460,6 +679,409 @@ static const struct dentry_operations proc_sys_dentry_operations = { .d_compare = proc_sys_compare, }; +static struct ctl_table *is_branch_in(struct ctl_table *branch, + struct ctl_table *table) +{ + struct ctl_table *p; + const char *s = branch->procname; + + /* branch should have named subdirectory as its first element */ + if (!s || !branch->child) + return NULL; + + /* ... and nothing else */ + if (branch[1].procname) + return NULL; + + /* table should contain subdirectory with the same name */ + for (p = table; p->procname; p++) { + if (!p->child) + continue; + if (p->procname && strcmp(p->procname, s) == 0) + return p; + } + return NULL; +} + +/* see if attaching q to p would be an improvement */ +static void try_attach(struct ctl_table_header *p, struct ctl_table_header *q) +{ + struct ctl_table *to = p->ctl_table, *by = q->ctl_table; + struct ctl_table *next; + int is_better = 0; + int not_in_parent = !p->attached_by; + + while ((next = is_branch_in(by, to)) != NULL) { + if (by == q->attached_by) + is_better = 1; + if (to == p->attached_by) + not_in_parent = 1; + by = by->child; + to = next->child; + } + + if (is_better && not_in_parent) { + q->attached_by = by; + q->attached_to = to; + q->parent = p; + } +} + +#ifdef CONFIG_SYSCTL_SYSCALL_CHECK +static int sysctl_depth(struct ctl_table *table) +{ + struct ctl_table *tmp; + int depth; + + depth = 0; + for (tmp = table; tmp->parent; tmp = tmp->parent) + depth++; + + return depth; +} + +static struct ctl_table *sysctl_parent(struct ctl_table *table, int n) +{ + int i; + + for (i = 0; table && i < n; i++) + table = table->parent; + + return table; +} + + +static void sysctl_print_path(struct ctl_table *table) +{ + struct ctl_table *tmp; + int depth, i; + depth = sysctl_depth(table); + if (table->procname) { + for (i = depth; i >= 0; i--) { + tmp = sysctl_parent(table, i); + printk("/%s", tmp->procname?tmp->procname:""); + } + } + printk(" "); +} + +static struct ctl_table *sysctl_check_lookup(struct nsproxy *namespaces, + struct ctl_table *table) +{ + struct ctl_table_header *head; + struct ctl_table *ref, *test; + int depth, cur_depth; + + depth = sysctl_depth(table); + + for (head = __sysctl_head_next(namespaces, NULL); head; + head = __sysctl_head_next(namespaces, head)) { + cur_depth = depth; + ref = head->ctl_table; +repeat: + test = sysctl_parent(table, cur_depth); + for (; ref->procname; ref++) { + int match = 0; + if (cur_depth && !ref->child) + continue; + + if (test->procname && ref->procname && + (strcmp(test->procname, ref->procname) == 0)) + match++; + + if (match) { + if (cur_depth != 0) { + cur_depth--; + ref = ref->child; + goto repeat; + } + goto out; + } + } + } + ref = NULL; +out: + sysctl_head_finish(head); + return ref; +} + +static void set_fail(const char **fail, struct ctl_table *table, const char *str) +{ + if (*fail) { + printk(KERN_ERR "sysctl table check failed: "); + sysctl_print_path(table); + printk(" %s\n", *fail); + dump_stack(); + } + *fail = str; +} + +static void sysctl_check_leaf(struct nsproxy *namespaces, + struct ctl_table *table, const char **fail) +{ + struct ctl_table *ref; + + ref = sysctl_check_lookup(namespaces, table); + if (ref && (ref != table)) + set_fail(fail, table, "Sysctl already exists"); +} + +static int sysctl_check_table(struct nsproxy *namespaces, struct ctl_table *table) +{ + int error = 0; + for (; table->procname; table++) { + const char *fail = NULL; + + if (table->parent) { + if (!table->parent->procname) + set_fail(&fail, table, "Parent without procname"); + } + if (table->child) { + if (table->data) + set_fail(&fail, table, "Directory with data?"); + if (table->maxlen) + set_fail(&fail, table, "Directory with maxlen?"); + if ((table->mode & (S_IRUGO|S_IXUGO)) != table->mode) + set_fail(&fail, table, "Writable sysctl directory"); + if (table->proc_handler) + set_fail(&fail, table, "Directory with proc_handler"); + if (table->extra1) + set_fail(&fail, table, "Directory with extra1"); + if (table->extra2) + set_fail(&fail, table, "Directory with extra2"); + } else { + if ((table->proc_handler == proc_dostring) || + (table->proc_handler == proc_dointvec) || + (table->proc_handler == proc_dointvec_minmax) || + (table->proc_handler == proc_dointvec_jiffies) || + (table->proc_handler == proc_dointvec_userhz_jiffies) || + (table->proc_handler == proc_dointvec_ms_jiffies) || + (table->proc_handler == proc_doulongvec_minmax) || + (table->proc_handler == proc_doulongvec_ms_jiffies_minmax)) { + if (!table->data) + set_fail(&fail, table, "No data"); + if (!table->maxlen) + set_fail(&fail, table, "No maxlen"); + } +#ifdef CONFIG_PROC_SYSCTL + if (!table->proc_handler) + set_fail(&fail, table, "No proc_handler"); +#endif + sysctl_check_leaf(namespaces, table, &fail); + } + if (table->mode > 0777) + set_fail(&fail, table, "bogus .mode"); + if (fail) { + set_fail(&fail, table, NULL); + error = -EINVAL; + } + if (table->child) + error |= sysctl_check_table(namespaces, table->child); + } + return error; +} +#endif /* CONFIG_SYSCTL_SYSCALL_CHECK */ + +/** + * __register_sysctl_paths - register a sysctl hierarchy + * @root: List of sysctl headers to register on + * @namespaces: Data to compute which lists of sysctl entries are visible + * @path: The path to the directory the sysctl table is in. + * @table: the top-level table structure + * + * Register a sysctl table hierarchy. @table should be a filled in ctl_table + * array. A completely 0 filled entry terminates the table. + * + * The members of the &struct ctl_table structure are used as follows: + * + * procname - the name of the sysctl file under /proc/sys. Set to %NULL to not + * enter a sysctl file + * + * data - a pointer to data for use by proc_handler + * + * maxlen - the maximum size in bytes of the data + * + * mode - the file permissions for the /proc/sys file, and for sysctl(2) + * + * child - a pointer to the child sysctl table if this entry is a directory, or + * %NULL. + * + * proc_handler - the text handler routine (described below) + * + * de - for internal use by the sysctl routines + * + * extra1, extra2 - extra pointers usable by the proc handler routines + * + * Leaf nodes in the sysctl tree will be represented by a single file + * under /proc; non-leaf nodes will be represented by directories. + * + * sysctl(2) can automatically manage read and write requests through + * the sysctl table. The data and maxlen fields of the ctl_table + * struct enable minimal validation of the values being written to be + * performed, and the mode field allows minimal authentication. + * + * There must be a proc_handler routine for any terminal nodes + * mirrored under /proc/sys (non-terminals are handled by a built-in + * directory handler). Several default handlers are available to + * cover common cases - + * + * proc_dostring(), proc_dointvec(), proc_dointvec_jiffies(), + * proc_dointvec_userhz_jiffies(), proc_dointvec_minmax(), + * proc_doulongvec_ms_jiffies_minmax(), proc_doulongvec_minmax() + * + * It is the handler's job to read the input buffer from user memory + * and process it. The handler should return 0 on success. + * + * This routine returns %NULL on a failure to register, and a pointer + * to the table header on success. + */ +struct ctl_table_header *__register_sysctl_paths( + struct ctl_table_root *root, + struct nsproxy *namespaces, + const struct ctl_path *path, struct ctl_table *table) +{ + struct ctl_table_header *header; + struct ctl_table *new, **prevp; + unsigned int n, npath; + struct ctl_table_set *set; + + /* Count the path components */ + for (npath = 0; path[npath].procname; ++npath) + ; + + /* + * For each path component, allocate a 2-element ctl_table array. + * The first array element will be filled with the sysctl entry + * for this, the second will be the sentinel (procname == 0). + * + * We allocate everything in one go so that we don't have to + * worry about freeing additional memory in unregister_sysctl_table. + */ + header = kzalloc(sizeof(struct ctl_table_header) + + (2 * npath * sizeof(struct ctl_table)), GFP_KERNEL); + if (!header) + return NULL; + + new = (struct ctl_table *) (header + 1); + + /* Now connect the dots */ + prevp = &header->ctl_table; + for (n = 0; n < npath; ++n, ++path) { + /* Copy the procname */ + new->procname = path->procname; + new->mode = 0555; + + *prevp = new; + prevp = &new->child; + + new += 2; + } + *prevp = table; + header->ctl_table_arg = table; + + INIT_LIST_HEAD(&header->ctl_entry); + header->used = 0; + header->unregistering = NULL; + header->root = root; + sysctl_set_parent(NULL, header->ctl_table); + header->count = 1; +#ifdef CONFIG_SYSCTL_SYSCALL_CHECK + if (sysctl_check_table(namespaces, header->ctl_table)) { + kfree(header); + return NULL; + } +#endif + spin_lock(&sysctl_lock); + header->set = lookup_header_set(root, namespaces); + header->attached_by = header->ctl_table; + header->attached_to = root_table; + header->parent = &root_table_header; + for (set = header->set; set; set = set->parent) { + struct ctl_table_header *p; + list_for_each_entry(p, &set->list, ctl_entry) { + if (p->unregistering) + continue; + try_attach(p, header); + } + } + header->parent->count++; + list_add_tail(&header->ctl_entry, &header->set->list); + spin_unlock(&sysctl_lock); + + return header; +} + +/** + * register_sysctl_table_path - register a sysctl table hierarchy + * @path: The path to the directory the sysctl table is in. + * @table: the top-level table structure + * + * Register a sysctl table hierarchy. @table should be a filled in ctl_table + * array. A completely 0 filled entry terminates the table. + * + * See __register_sysctl_paths for more details. + */ +struct ctl_table_header *register_sysctl_paths(const struct ctl_path *path, + struct ctl_table *table) +{ + return __register_sysctl_paths(&sysctl_table_root, current->nsproxy, + path, table); +} +EXPORT_SYMBOL(register_sysctl_paths); + +/** + * register_sysctl_table - register a sysctl table hierarchy + * @table: the top-level table structure + * + * Register a sysctl table hierarchy. @table should be a filled in ctl_table + * array. A completely 0 filled entry terminates the table. + * + * See register_sysctl_paths for more details. + */ +struct ctl_table_header *register_sysctl_table(struct ctl_table *table) +{ + static const struct ctl_path null_path[] = { {} }; + + return register_sysctl_paths(null_path, table); +} +EXPORT_SYMBOL(register_sysctl_table); + +/** + * unregister_sysctl_table - unregister a sysctl table hierarchy + * @header: the header returned from register_sysctl_table + * + * Unregisters the sysctl table and all children. proc entries may not + * actually be removed until they are no longer used by anyone. + */ +void unregister_sysctl_table(struct ctl_table_header * header) +{ + might_sleep(); + + if (header == NULL) + return; + + spin_lock(&sysctl_lock); + start_unregistering(header); + if (!--header->parent->count) { + WARN_ON(1); + kfree_rcu(header->parent, rcu); + } + if (!--header->count) + kfree_rcu(header, rcu); + spin_unlock(&sysctl_lock); +} +EXPORT_SYMBOL(unregister_sysctl_table); + +void setup_sysctl_set(struct ctl_table_set *p, + struct ctl_table_set *parent, + int (*is_seen)(struct ctl_table_set *)) +{ + INIT_LIST_HEAD(&p->list); + p->parent = parent ? parent : &sysctl_table_root.default_set; + p->is_seen = is_seen; +} + + int __init proc_sys_init(void) { struct proc_dir_entry *proc_sys_root; diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index 5e3532e9599f..08cabbfddacb 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -1073,17 +1073,6 @@ extern void setup_sysctl_set(struct ctl_table_set *p, struct ctl_table_set *parent, int (*is_seen)(struct ctl_table_set *)); -extern void sysctl_head_get(struct ctl_table_header *); -extern void sysctl_head_put(struct ctl_table_header *); -extern int sysctl_is_seen(struct ctl_table_header *); -extern struct ctl_table_header *sysctl_head_grab(struct ctl_table_header *); -extern struct ctl_table_header *sysctl_head_next(struct ctl_table_header *prev); -extern struct ctl_table_header *__sysctl_head_next(struct nsproxy *namespaces, - struct ctl_table_header *prev); -extern void sysctl_head_finish(struct ctl_table_header *prev); -extern int sysctl_perm(struct ctl_table_root *root, - struct ctl_table *table, int op); - void register_sysctl_root(struct ctl_table_root *root); struct ctl_table_header *__register_sysctl_paths( struct ctl_table_root *root, struct nsproxy *namespaces, @@ -1093,7 +1082,6 @@ struct ctl_table_header *register_sysctl_paths(const struct ctl_path *path, struct ctl_table *table); void unregister_sysctl_table(struct ctl_table_header * table); -int sysctl_check_table(struct nsproxy *namespaces, struct ctl_table *table); extern int sysctl_init(void); #else /* CONFIG_SYSCTL */ @@ -1118,10 +1106,6 @@ static inline void setup_sysctl_set(struct ctl_table_set *p, { } -static inline void sysctl_head_put(struct ctl_table_header *head) -{ -} - #endif /* CONFIG_SYSCTL */ #endif /* __KERNEL__ */ diff --git a/kernel/Makefile b/kernel/Makefile index 2d9de86b7e76..cb41b9547c9f 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -27,7 +27,6 @@ obj-y += power/ obj-$(CONFIG_FREEZER) += freezer.o obj-$(CONFIG_PROFILING) += profile.o -obj-$(CONFIG_SYSCTL_SYSCALL_CHECK) += sysctl_check.o obj-$(CONFIG_STACKTRACE) += stacktrace.o obj-y += time/ obj-$(CONFIG_DEBUG_MUTEXES) += mutex-debug.o diff --git a/kernel/sysctl.c b/kernel/sysctl.c index ad460248acc7..b774909ed46c 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -192,20 +192,6 @@ static int sysrq_sysctl_handler(ctl_table *table, int write, #endif -static struct ctl_table root_table[1]; -static struct ctl_table_root sysctl_table_root; -static struct ctl_table_header root_table_header = { - {{.count = 1, - .ctl_table = root_table, - .ctl_entry = LIST_HEAD_INIT(sysctl_table_root.default_set.list),}}, - .root = &sysctl_table_root, - .set = &sysctl_table_root.default_set, -}; -static struct ctl_table_root sysctl_table_root = { - .root_list = LIST_HEAD_INIT(sysctl_table_root.root_list), - .default_set.list = LIST_HEAD_INIT(root_table_header.ctl_entry), -}; - static struct ctl_table kern_table[]; static struct ctl_table vm_table[]; static struct ctl_table fs_table[]; @@ -1559,459 +1545,12 @@ static struct ctl_table dev_table[] = { { } }; -static DEFINE_SPINLOCK(sysctl_lock); - -/* called under sysctl_lock */ -static int use_table(struct ctl_table_header *p) -{ - if (unlikely(p->unregistering)) - return 0; - p->used++; - return 1; -} - -/* called under sysctl_lock */ -static void unuse_table(struct ctl_table_header *p) -{ - if (!--p->used) - if (unlikely(p->unregistering)) - complete(p->unregistering); -} - -/* called under sysctl_lock, will reacquire if has to wait */ -static void start_unregistering(struct ctl_table_header *p) -{ - /* - * if p->used is 0, nobody will ever touch that entry again; - * we'll eliminate all paths to it before dropping sysctl_lock - */ - if (unlikely(p->used)) { - struct completion wait; - init_completion(&wait); - p->unregistering = &wait; - spin_unlock(&sysctl_lock); - wait_for_completion(&wait); - spin_lock(&sysctl_lock); - } else { - /* anything non-NULL; we'll never dereference it */ - p->unregistering = ERR_PTR(-EINVAL); - } - /* - * do not remove from the list until nobody holds it; walking the - * list in do_sysctl() relies on that. - */ - list_del_init(&p->ctl_entry); -} - -void sysctl_head_get(struct ctl_table_header *head) -{ - spin_lock(&sysctl_lock); - head->count++; - spin_unlock(&sysctl_lock); -} - -void sysctl_head_put(struct ctl_table_header *head) -{ - spin_lock(&sysctl_lock); - if (!--head->count) - kfree_rcu(head, rcu); - spin_unlock(&sysctl_lock); -} - -struct ctl_table_header *sysctl_head_grab(struct ctl_table_header *head) -{ - if (!head) - BUG(); - spin_lock(&sysctl_lock); - if (!use_table(head)) - head = ERR_PTR(-ENOENT); - spin_unlock(&sysctl_lock); - return head; -} - -void sysctl_head_finish(struct ctl_table_header *head) -{ - if (!head) - return; - spin_lock(&sysctl_lock); - unuse_table(head); - spin_unlock(&sysctl_lock); -} - -static struct ctl_table_set * -lookup_header_set(struct ctl_table_root *root, struct nsproxy *namespaces) -{ - struct ctl_table_set *set = &root->default_set; - if (root->lookup) - set = root->lookup(root, namespaces); - return set; -} - -static struct list_head * -lookup_header_list(struct ctl_table_root *root, struct nsproxy *namespaces) -{ - struct ctl_table_set *set = lookup_header_set(root, namespaces); - return &set->list; -} - -struct ctl_table_header *__sysctl_head_next(struct nsproxy *namespaces, - struct ctl_table_header *prev) -{ - struct ctl_table_root *root; - struct list_head *header_list; - struct ctl_table_header *head; - struct list_head *tmp; - - spin_lock(&sysctl_lock); - if (prev) { - head = prev; - tmp = &prev->ctl_entry; - unuse_table(prev); - goto next; - } - tmp = &root_table_header.ctl_entry; - for (;;) { - head = list_entry(tmp, struct ctl_table_header, ctl_entry); - - if (!use_table(head)) - goto next; - spin_unlock(&sysctl_lock); - return head; - next: - root = head->root; - tmp = tmp->next; - header_list = lookup_header_list(root, namespaces); - if (tmp != header_list) - continue; - - do { - root = list_entry(root->root_list.next, - struct ctl_table_root, root_list); - if (root == &sysctl_table_root) - goto out; - header_list = lookup_header_list(root, namespaces); - } while (list_empty(header_list)); - tmp = header_list->next; - } -out: - spin_unlock(&sysctl_lock); - return NULL; -} - -struct ctl_table_header *sysctl_head_next(struct ctl_table_header *prev) -{ - return __sysctl_head_next(current->nsproxy, prev); -} - -void register_sysctl_root(struct ctl_table_root *root) -{ - spin_lock(&sysctl_lock); - list_add_tail(&root->root_list, &sysctl_table_root.root_list); - spin_unlock(&sysctl_lock); -} - -/* - * sysctl_perm does NOT grant the superuser all rights automatically, because - * some sysctl variables are readonly even to root. - */ - -static int test_perm(int mode, int op) -{ - if (!current_euid()) - mode >>= 6; - else if (in_egroup_p(0)) - mode >>= 3; - if ((op & ~mode & (MAY_READ|MAY_WRITE|MAY_EXEC)) == 0) - return 0; - return -EACCES; -} - -int sysctl_perm(struct ctl_table_root *root, struct ctl_table *table, int op) -{ - int mode; - - if (root->permissions) - mode = root->permissions(root, current->nsproxy, table); - else - mode = table->mode; - - return test_perm(mode, op); -} - -static void sysctl_set_parent(struct ctl_table *parent, struct ctl_table *table) -{ - for (; table->procname; table++) { - table->parent = parent; - if (table->child) - sysctl_set_parent(table, table->child); - } -} - int __init sysctl_init(void) { register_sysctl_table(sysctl_base_table); return 0; } -static struct ctl_table *is_branch_in(struct ctl_table *branch, - struct ctl_table *table) -{ - struct ctl_table *p; - const char *s = branch->procname; - - /* branch should have named subdirectory as its first element */ - if (!s || !branch->child) - return NULL; - - /* ... and nothing else */ - if (branch[1].procname) - return NULL; - - /* table should contain subdirectory with the same name */ - for (p = table; p->procname; p++) { - if (!p->child) - continue; - if (p->procname && strcmp(p->procname, s) == 0) - return p; - } - return NULL; -} - -/* see if attaching q to p would be an improvement */ -static void try_attach(struct ctl_table_header *p, struct ctl_table_header *q) -{ - struct ctl_table *to = p->ctl_table, *by = q->ctl_table; - struct ctl_table *next; - int is_better = 0; - int not_in_parent = !p->attached_by; - - while ((next = is_branch_in(by, to)) != NULL) { - if (by == q->attached_by) - is_better = 1; - if (to == p->attached_by) - not_in_parent = 1; - by = by->child; - to = next->child; - } - - if (is_better && not_in_parent) { - q->attached_by = by; - q->attached_to = to; - q->parent = p; - } -} - -/** - * __register_sysctl_paths - register a sysctl hierarchy - * @root: List of sysctl headers to register on - * @namespaces: Data to compute which lists of sysctl entries are visible - * @path: The path to the directory the sysctl table is in. - * @table: the top-level table structure - * - * Register a sysctl table hierarchy. @table should be a filled in ctl_table - * array. A completely 0 filled entry terminates the table. - * - * The members of the &struct ctl_table structure are used as follows: - * - * procname - the name of the sysctl file under /proc/sys. Set to %NULL to not - * enter a sysctl file - * - * data - a pointer to data for use by proc_handler - * - * maxlen - the maximum size in bytes of the data - * - * mode - the file permissions for the /proc/sys file, and for sysctl(2) - * - * child - a pointer to the child sysctl table if this entry is a directory, or - * %NULL. - * - * proc_handler - the text handler routine (described below) - * - * de - for internal use by the sysctl routines - * - * extra1, extra2 - extra pointers usable by the proc handler routines - * - * Leaf nodes in the sysctl tree will be represented by a single file - * under /proc; non-leaf nodes will be represented by directories. - * - * sysctl(2) can automatically manage read and write requests through - * the sysctl table. The data and maxlen fields of the ctl_table - * struct enable minimal validation of the values being written to be - * performed, and the mode field allows minimal authentication. - * - * There must be a proc_handler routine for any terminal nodes - * mirrored under /proc/sys (non-terminals are handled by a built-in - * directory handler). Several default handlers are available to - * cover common cases - - * - * proc_dostring(), proc_dointvec(), proc_dointvec_jiffies(), - * proc_dointvec_userhz_jiffies(), proc_dointvec_minmax(), - * proc_doulongvec_ms_jiffies_minmax(), proc_doulongvec_minmax() - * - * It is the handler's job to read the input buffer from user memory - * and process it. The handler should return 0 on success. - * - * This routine returns %NULL on a failure to register, and a pointer - * to the table header on success. - */ -struct ctl_table_header *__register_sysctl_paths( - struct ctl_table_root *root, - struct nsproxy *namespaces, - const struct ctl_path *path, struct ctl_table *table) -{ - struct ctl_table_header *header; - struct ctl_table *new, **prevp; - unsigned int n, npath; - struct ctl_table_set *set; - - /* Count the path components */ - for (npath = 0; path[npath].procname; ++npath) - ; - - /* - * For each path component, allocate a 2-element ctl_table array. - * The first array element will be filled with the sysctl entry - * for this, the second will be the sentinel (procname == 0). - * - * We allocate everything in one go so that we don't have to - * worry about freeing additional memory in unregister_sysctl_table. - */ - header = kzalloc(sizeof(struct ctl_table_header) + - (2 * npath * sizeof(struct ctl_table)), GFP_KERNEL); - if (!header) - return NULL; - - new = (struct ctl_table *) (header + 1); - - /* Now connect the dots */ - prevp = &header->ctl_table; - for (n = 0; n < npath; ++n, ++path) { - /* Copy the procname */ - new->procname = path->procname; - new->mode = 0555; - - *prevp = new; - prevp = &new->child; - - new += 2; - } - *prevp = table; - header->ctl_table_arg = table; - - INIT_LIST_HEAD(&header->ctl_entry); - header->used = 0; - header->unregistering = NULL; - header->root = root; - sysctl_set_parent(NULL, header->ctl_table); - header->count = 1; -#ifdef CONFIG_SYSCTL_SYSCALL_CHECK - if (sysctl_check_table(namespaces, header->ctl_table)) { - kfree(header); - return NULL; - } -#endif - spin_lock(&sysctl_lock); - header->set = lookup_header_set(root, namespaces); - header->attached_by = header->ctl_table; - header->attached_to = root_table; - header->parent = &root_table_header; - for (set = header->set; set; set = set->parent) { - struct ctl_table_header *p; - list_for_each_entry(p, &set->list, ctl_entry) { - if (p->unregistering) - continue; - try_attach(p, header); - } - } - header->parent->count++; - list_add_tail(&header->ctl_entry, &header->set->list); - spin_unlock(&sysctl_lock); - - return header; -} - -/** - * register_sysctl_table_path - register a sysctl table hierarchy - * @path: The path to the directory the sysctl table is in. - * @table: the top-level table structure - * - * Register a sysctl table hierarchy. @table should be a filled in ctl_table - * array. A completely 0 filled entry terminates the table. - * - * See __register_sysctl_paths for more details. - */ -struct ctl_table_header *register_sysctl_paths(const struct ctl_path *path, - struct ctl_table *table) -{ - return __register_sysctl_paths(&sysctl_table_root, current->nsproxy, - path, table); -} - -/** - * register_sysctl_table - register a sysctl table hierarchy - * @table: the top-level table structure - * - * Register a sysctl table hierarchy. @table should be a filled in ctl_table - * array. A completely 0 filled entry terminates the table. - * - * See register_sysctl_paths for more details. - */ -struct ctl_table_header *register_sysctl_table(struct ctl_table *table) -{ - static const struct ctl_path null_path[] = { {} }; - - return register_sysctl_paths(null_path, table); -} - -/** - * unregister_sysctl_table - unregister a sysctl table hierarchy - * @header: the header returned from register_sysctl_table - * - * Unregisters the sysctl table and all children. proc entries may not - * actually be removed until they are no longer used by anyone. - */ -void unregister_sysctl_table(struct ctl_table_header * header) -{ - might_sleep(); - - if (header == NULL) - return; - - spin_lock(&sysctl_lock); - start_unregistering(header); - if (!--header->parent->count) { - WARN_ON(1); - kfree_rcu(header->parent, rcu); - } - if (!--header->count) - kfree_rcu(header, rcu); - spin_unlock(&sysctl_lock); -} - -int sysctl_is_seen(struct ctl_table_header *p) -{ - struct ctl_table_set *set = p->set; - int res; - spin_lock(&sysctl_lock); - if (p->unregistering) - res = 0; - else if (!set->is_seen) - res = 1; - else - res = set->is_seen(set); - spin_unlock(&sysctl_lock); - return res; -} - -void setup_sysctl_set(struct ctl_table_set *p, - struct ctl_table_set *parent, - int (*is_seen)(struct ctl_table_set *)) -{ - INIT_LIST_HEAD(&p->list); - p->parent = parent ? parent : &sysctl_table_root.default_set; - p->is_seen = is_seen; -} - #endif /* CONFIG_SYSCTL */ /* @@ -2977,6 +2516,3 @@ EXPORT_SYMBOL(proc_dointvec_ms_jiffies); EXPORT_SYMBOL(proc_dostring); EXPORT_SYMBOL(proc_doulongvec_minmax); EXPORT_SYMBOL(proc_doulongvec_ms_jiffies_minmax); -EXPORT_SYMBOL(register_sysctl_table); -EXPORT_SYMBOL(register_sysctl_paths); -EXPORT_SYMBOL(unregister_sysctl_table); diff --git a/kernel/sysctl_check.c b/kernel/sysctl_check.c deleted file mode 100644 index 362da653813d..000000000000 --- a/kernel/sysctl_check.c +++ /dev/null @@ -1,160 +0,0 @@ -#include -#include -#include "../fs/xfs/xfs_sysctl.h" -#include -#include -#include - - -static int sysctl_depth(struct ctl_table *table) -{ - struct ctl_table *tmp; - int depth; - - depth = 0; - for (tmp = table; tmp->parent; tmp = tmp->parent) - depth++; - - return depth; -} - -static struct ctl_table *sysctl_parent(struct ctl_table *table, int n) -{ - int i; - - for (i = 0; table && i < n; i++) - table = table->parent; - - return table; -} - - -static void sysctl_print_path(struct ctl_table *table) -{ - struct ctl_table *tmp; - int depth, i; - depth = sysctl_depth(table); - if (table->procname) { - for (i = depth; i >= 0; i--) { - tmp = sysctl_parent(table, i); - printk("/%s", tmp->procname?tmp->procname:""); - } - } - printk(" "); -} - -static struct ctl_table *sysctl_check_lookup(struct nsproxy *namespaces, - struct ctl_table *table) -{ - struct ctl_table_header *head; - struct ctl_table *ref, *test; - int depth, cur_depth; - - depth = sysctl_depth(table); - - for (head = __sysctl_head_next(namespaces, NULL); head; - head = __sysctl_head_next(namespaces, head)) { - cur_depth = depth; - ref = head->ctl_table; -repeat: - test = sysctl_parent(table, cur_depth); - for (; ref->procname; ref++) { - int match = 0; - if (cur_depth && !ref->child) - continue; - - if (test->procname && ref->procname && - (strcmp(test->procname, ref->procname) == 0)) - match++; - - if (match) { - if (cur_depth != 0) { - cur_depth--; - ref = ref->child; - goto repeat; - } - goto out; - } - } - } - ref = NULL; -out: - sysctl_head_finish(head); - return ref; -} - -static void set_fail(const char **fail, struct ctl_table *table, const char *str) -{ - if (*fail) { - printk(KERN_ERR "sysctl table check failed: "); - sysctl_print_path(table); - printk(" %s\n", *fail); - dump_stack(); - } - *fail = str; -} - -static void sysctl_check_leaf(struct nsproxy *namespaces, - struct ctl_table *table, const char **fail) -{ - struct ctl_table *ref; - - ref = sysctl_check_lookup(namespaces, table); - if (ref && (ref != table)) - set_fail(fail, table, "Sysctl already exists"); -} - -int sysctl_check_table(struct nsproxy *namespaces, struct ctl_table *table) -{ - int error = 0; - for (; table->procname; table++) { - const char *fail = NULL; - - if (table->parent) { - if (!table->parent->procname) - set_fail(&fail, table, "Parent without procname"); - } - if (table->child) { - if (table->data) - set_fail(&fail, table, "Directory with data?"); - if (table->maxlen) - set_fail(&fail, table, "Directory with maxlen?"); - if ((table->mode & (S_IRUGO|S_IXUGO)) != table->mode) - set_fail(&fail, table, "Writable sysctl directory"); - if (table->proc_handler) - set_fail(&fail, table, "Directory with proc_handler"); - if (table->extra1) - set_fail(&fail, table, "Directory with extra1"); - if (table->extra2) - set_fail(&fail, table, "Directory with extra2"); - } else { - if ((table->proc_handler == proc_dostring) || - (table->proc_handler == proc_dointvec) || - (table->proc_handler == proc_dointvec_minmax) || - (table->proc_handler == proc_dointvec_jiffies) || - (table->proc_handler == proc_dointvec_userhz_jiffies) || - (table->proc_handler == proc_dointvec_ms_jiffies) || - (table->proc_handler == proc_doulongvec_minmax) || - (table->proc_handler == proc_doulongvec_ms_jiffies_minmax)) { - if (!table->data) - set_fail(&fail, table, "No data"); - if (!table->maxlen) - set_fail(&fail, table, "No maxlen"); - } -#ifdef CONFIG_PROC_SYSCTL - if (!table->proc_handler) - set_fail(&fail, table, "No proc_handler"); -#endif - sysctl_check_leaf(namespaces, table, &fail); - } - if (table->mode > 0777) - set_fail(&fail, table, "bogus .mode"); - if (fail) { - set_fail(&fail, table, NULL); - error = -EINVAL; - } - if (table->child) - error |= sysctl_check_table(namespaces, table->child); - } - return error; -} -- cgit v1.2.3 From 97324cd804b7b9fb6044e114329335db79810425 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Mon, 9 Jan 2012 22:19:13 -0800 Subject: sysctl: Implement retire_sysctl_set This adds a small helper retire_sysctl_set to remove the intimate knowledge about the how a sysctl_set is implemented from net/sysct_net.c Signed-off-by: Eric W. Biederman --- fs/proc/proc_sysctl.c | 4 ++++ include/linux/sysctl.h | 1 + net/sysctl_net.c | 2 +- 3 files changed, 6 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c index f6aa75111b41..9d8223cd3655 100644 --- a/fs/proc/proc_sysctl.c +++ b/fs/proc/proc_sysctl.c @@ -1080,6 +1080,10 @@ void setup_sysctl_set(struct ctl_table_set *p, p->is_seen = is_seen; } +void retire_sysctl_set(struct ctl_table_set *set) +{ + WARN_ON(!list_empty(&set->list)); +} int __init proc_sys_init(void) { diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index 08cabbfddacb..475ff0e35e63 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -1072,6 +1072,7 @@ void proc_sys_poll_notify(struct ctl_table_poll *poll); extern void setup_sysctl_set(struct ctl_table_set *p, struct ctl_table_set *parent, int (*is_seen)(struct ctl_table_set *)); +extern void retire_sysctl_set(struct ctl_table_set *set); void register_sysctl_root(struct ctl_table_root *root); struct ctl_table_header *__register_sysctl_paths( diff --git a/net/sysctl_net.c b/net/sysctl_net.c index a6bbee2bc710..ffd67a6515a3 100644 --- a/net/sysctl_net.c +++ b/net/sysctl_net.c @@ -82,7 +82,7 @@ static int __net_init sysctl_net_init(struct net *net) static void __net_exit sysctl_net_exit(struct net *net) { - WARN_ON(!list_empty(&net->sysctls.list)); + retire_sysctl_set(&net->sysctls); } static struct pernet_operations sysctl_pernet_ops = { -- cgit v1.2.3 From bd295b56cfae85f2dd6c2b03951480c91e6d08f3 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Sun, 22 Jan 2012 21:10:21 -0800 Subject: sysctl: Remove the unnecessary sysctl_set parent concept. In sysctl_net register the two networking roots in the proper order. In register_sysctl walk the sysctl sets in the reverse order of the sysctl roots. Remove parent from ctl_table_set and setup_sysctl_set as it is no longer needed. Signed-off-by: Eric W. Biederman --- fs/proc/proc_sysctl.c | 11 ++++++++--- include/linux/sysctl.h | 3 --- net/sysctl_net.c | 5 ++--- 3 files changed, 10 insertions(+), 9 deletions(-) (limited to 'include/linux') diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c index 9d8223cd3655..86d32a318e2c 100644 --- a/fs/proc/proc_sysctl.c +++ b/fs/proc/proc_sysctl.c @@ -995,13 +995,20 @@ struct ctl_table_header *__register_sysctl_paths( header->attached_by = header->ctl_table; header->attached_to = root_table; header->parent = &root_table_header; - for (set = header->set; set; set = set->parent) { + set = header->set; + root = header->root; + for (;;) { struct ctl_table_header *p; list_for_each_entry(p, &set->list, ctl_entry) { if (p->unregistering) continue; try_attach(p, header); } + if (root == &sysctl_table_root) + break; + root = list_entry(root->root_list.prev, + struct ctl_table_root, root_list); + set = lookup_header_set(root, namespaces); } header->parent->count++; list_add_tail(&header->ctl_entry, &header->set->list); @@ -1072,11 +1079,9 @@ void unregister_sysctl_table(struct ctl_table_header * header) EXPORT_SYMBOL(unregister_sysctl_table); void setup_sysctl_set(struct ctl_table_set *p, - struct ctl_table_set *parent, int (*is_seen)(struct ctl_table_set *)) { INIT_LIST_HEAD(&p->list); - p->parent = parent ? parent : &sysctl_table_root.default_set; p->is_seen = is_seen; } diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index 475ff0e35e63..43c36acdb628 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -1047,7 +1047,6 @@ struct ctl_table_header struct ctl_table_set { struct list_head list; - struct ctl_table_set *parent; int (*is_seen)(struct ctl_table_set *); }; @@ -1070,7 +1069,6 @@ struct ctl_path { void proc_sys_poll_notify(struct ctl_table_poll *poll); extern void setup_sysctl_set(struct ctl_table_set *p, - struct ctl_table_set *parent, int (*is_seen)(struct ctl_table_set *)); extern void retire_sysctl_set(struct ctl_table_set *set); @@ -1102,7 +1100,6 @@ static inline void unregister_sysctl_table(struct ctl_table_header * table) } static inline void setup_sysctl_set(struct ctl_table_set *p, - struct ctl_table_set *parent, int (*is_seen)(struct ctl_table_set *)) { } diff --git a/net/sysctl_net.c b/net/sysctl_net.c index ffd67a6515a3..07c6b879c8b2 100644 --- a/net/sysctl_net.c +++ b/net/sysctl_net.c @@ -75,7 +75,6 @@ static struct ctl_table_root net_sysctl_ro_root = { static int __net_init sysctl_net_init(struct net *net) { setup_sysctl_set(&net->sysctls, - &net_sysctl_ro_root.default_set, is_seen); return 0; } @@ -96,9 +95,9 @@ static __init int net_sysctl_init(void) ret = register_pernet_subsys(&sysctl_pernet_ops); if (ret) goto out; - register_sysctl_root(&net_sysctl_root); - setup_sysctl_set(&net_sysctl_ro_root.default_set, NULL, NULL); + setup_sysctl_set(&net_sysctl_ro_root.default_set, NULL); register_sysctl_root(&net_sysctl_ro_root); + register_sysctl_root(&net_sysctl_root); out: return ret; } -- cgit v1.2.3 From 6e9d5164153ad6539edd31e7afb02a3e79124cad Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Sat, 21 Jan 2012 10:26:26 -0800 Subject: sysctl: Add support for register sysctl tables with a normal cstring path. Make __register_sysctl_table the core sysctl registration operation and make it take a char * string as path. Now that binary paths have been banished into the real of backwards compatibility in kernel/binary_sysctl.c where they can be safely ignored there is no longer a need to use struct ctl_path to represent path names when registering ctl_tables. Start the transition to using normal char * strings to represent pathnames when registering sysctl tables. Normal strings are easier to deal with both in the internal sysctl implementation and for programmers registering sysctl tables. __register_sysctl_paths is turned into a backwards compatibility wrapper that converts a ctl_path array into a normal char * string. Signed-off-by: Eric W. Biederman --- fs/proc/proc_sysctl.c | 94 ++++++++++++++++++++++++++++++++++++++++++++------ include/linux/sysctl.h | 3 ++ 2 files changed, 87 insertions(+), 10 deletions(-) (limited to 'include/linux') diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c index bcf60fb8dce5..5704ff0e889f 100644 --- a/fs/proc/proc_sysctl.c +++ b/fs/proc/proc_sysctl.c @@ -882,7 +882,7 @@ static int sysctl_check_table(struct nsproxy *namespaces, struct ctl_table *tabl #endif /* CONFIG_SYSCTL_SYSCALL_CHECK */ /** - * __register_sysctl_paths - register a sysctl hierarchy + * __register_sysctl_table - register a sysctl table * @root: List of sysctl headers to register on * @namespaces: Data to compute which lists of sysctl entries are visible * @path: The path to the directory the sysctl table is in. @@ -934,21 +934,34 @@ static int sysctl_check_table(struct nsproxy *namespaces, struct ctl_table *tabl * This routine returns %NULL on a failure to register, and a pointer * to the table header on success. */ -struct ctl_table_header *__register_sysctl_paths( +struct ctl_table_header *__register_sysctl_table( struct ctl_table_root *root, struct nsproxy *namespaces, - const struct ctl_path *path, struct ctl_table *table) + const char *path, struct ctl_table *table) { struct ctl_table_header *header; struct ctl_table *new, **prevp; - unsigned int n, npath; + const char *name, *nextname; + unsigned int npath = 0; struct ctl_table_set *set; size_t path_bytes = 0; char *new_name; /* Count the path components */ - for (npath = 0; path[npath].procname; ++npath) - path_bytes += strlen(path[npath].procname) + 1; + for (name = path; name; name = nextname) { + int namelen; + nextname = strchr(name, '/'); + if (nextname) { + namelen = nextname - name; + nextname++; + } else { + namelen = strlen(name); + } + if (namelen == 0) + continue; + path_bytes += namelen + 1; + npath++; + } /* * For each path component, allocate a 2-element ctl_table array. @@ -968,9 +981,20 @@ struct ctl_table_header *__register_sysctl_paths( /* Now connect the dots */ prevp = &header->ctl_table; - for (n = 0; n < npath; ++n, ++path) { - /* Copy the procname */ - strcpy(new_name, path->procname); + for (name = path; name; name = nextname) { + int namelen; + nextname = strchr(name, '/'); + if (nextname) { + namelen = nextname - name; + nextname++; + } else { + namelen = strlen(name); + } + if (namelen == 0) + continue; + memcpy(new_name, name, namelen); + new_name[namelen] = '\0'; + new->procname = new_name; new->mode = 0555; @@ -978,7 +1002,7 @@ struct ctl_table_header *__register_sysctl_paths( prevp = &new->child; new += 2; - new_name += strlen(new_name) + 1; + new_name += namelen + 1; } *prevp = table; header->ctl_table_arg = table; @@ -1022,6 +1046,56 @@ struct ctl_table_header *__register_sysctl_paths( return header; } +static char *append_path(const char *path, char *pos, const char *name) +{ + int namelen; + namelen = strlen(name); + if (((pos - path) + namelen + 2) >= PATH_MAX) + return NULL; + memcpy(pos, name, namelen); + pos[namelen] = '/'; + pos[namelen + 1] = '\0'; + pos += namelen + 1; + return pos; +} + +/** + * __register_sysctl_paths - register a sysctl table hierarchy + * @root: List of sysctl headers to register on + * @namespaces: Data to compute which lists of sysctl entries are visible + * @path: The path to the directory the sysctl table is in. + * @table: the top-level table structure + * + * Register a sysctl table hierarchy. @table should be a filled in ctl_table + * array. A completely 0 filled entry terminates the table. + * + * See __register_sysctl_table for more details. + */ +struct ctl_table_header *__register_sysctl_paths( + struct ctl_table_root *root, + struct nsproxy *namespaces, + const struct ctl_path *path, struct ctl_table *table) +{ + struct ctl_table_header *header = NULL; + const struct ctl_path *component; + char *new_path, *pos; + + pos = new_path = kmalloc(PATH_MAX, GFP_KERNEL); + if (!new_path) + return NULL; + + pos[0] = '\0'; + for (component = path; component->procname; component++) { + pos = append_path(new_path, pos, component->procname); + if (!pos) + goto out; + } + header = __register_sysctl_table(root, namespaces, new_path, table); +out: + kfree(new_path); + return header; +} + /** * register_sysctl_table_path - register a sysctl table hierarchy * @path: The path to the directory the sysctl table is in. diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index 43c36acdb628..a514e0f6056d 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -1073,6 +1073,9 @@ extern void setup_sysctl_set(struct ctl_table_set *p, extern void retire_sysctl_set(struct ctl_table_set *set); void register_sysctl_root(struct ctl_table_root *root); +struct ctl_table_header *__register_sysctl_table( + struct ctl_table_root *root, struct nsproxy *namespaces, + const char *path, struct ctl_table *table); struct ctl_table_header *__register_sysctl_paths( struct ctl_table_root *root, struct nsproxy *namespaces, const struct ctl_path *path, struct ctl_table *table); -- cgit v1.2.3 From f728019bb72e655680c02ad1829323054a8e875f Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Sun, 22 Jan 2012 18:22:05 -0800 Subject: sysctl: register only tables of sysctl files Split the registration of a complex ctl_table array which may have arbitrary numbers of directories (->child != NULL) and tables of files into a series of simpler registrations that only register tables of files. Graphically: register('dir', { + file-a + file-b + subdir1 + file-c + subdir2 + file-d + file-e }) is transformed into: wrapper->subheaders[0] = register('dir', {file1-a, file1-b}) wrapper->subheaders[1] = register('dir/subdir1', {file-c}) wrapper->subheaders[2] = register('dir/subdir2', {file-d, file-e}) return wrapper This guarantees that __register_sysctl_table will only see a simple ctl_table array with all entries having (->child == NULL). Care was taken to pass the original simple ctl_table arrays to __register_sysctl_table whenever possible. This change is derived from a similar patch written by Lucrian Grijincu. Inspired-by: Lucian Adrian Grijincu Signed-off-by: Eric W. Biederman --- fs/proc/proc_sysctl.c | 165 +++++++++++++++++++++++++++++++++++++++++++------ include/linux/sysctl.h | 2 +- 2 files changed, 148 insertions(+), 19 deletions(-) (limited to 'include/linux') diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c index 9b91deeeb56c..6bab2ae9e395 100644 --- a/fs/proc/proc_sysctl.c +++ b/fs/proc/proc_sysctl.c @@ -882,7 +882,7 @@ static int sysctl_check_table(struct nsproxy *namespaces, struct ctl_table *tabl #endif /* CONFIG_SYSCTL_SYSCALL_CHECK */ /** - * __register_sysctl_table - register a sysctl table + * __register_sysctl_table - register a leaf sysctl table * @root: List of sysctl headers to register on * @namespaces: Data to compute which lists of sysctl entries are visible * @path: The path to the directory the sysctl table is in. @@ -900,29 +900,19 @@ static int sysctl_check_table(struct nsproxy *namespaces, struct ctl_table *tabl * * maxlen - the maximum size in bytes of the data * - * mode - the file permissions for the /proc/sys file, and for sysctl(2) + * mode - the file permissions for the /proc/sys file * - * child - a pointer to the child sysctl table if this entry is a directory, or - * %NULL. + * child - must be %NULL. * * proc_handler - the text handler routine (described below) * - * de - for internal use by the sysctl routines - * * extra1, extra2 - extra pointers usable by the proc handler routines * * Leaf nodes in the sysctl tree will be represented by a single file * under /proc; non-leaf nodes will be represented by directories. * - * sysctl(2) can automatically manage read and write requests through - * the sysctl table. The data and maxlen fields of the ctl_table - * struct enable minimal validation of the values being written to be - * performed, and the mode field allows minimal authentication. - * - * There must be a proc_handler routine for any terminal nodes - * mirrored under /proc/sys (non-terminals are handled by a built-in - * directory handler). Several default handlers are available to - * cover common cases - + * There must be a proc_handler routine for any terminal nodes. + * Several default handlers are available to cover common cases - * * proc_dostring(), proc_dointvec(), proc_dointvec_jiffies(), * proc_dointvec_userhz_jiffies(), proc_dointvec_minmax(), @@ -1059,6 +1049,100 @@ static char *append_path(const char *path, char *pos, const char *name) return pos; } +static int count_subheaders(struct ctl_table *table) +{ + int has_files = 0; + int nr_subheaders = 0; + struct ctl_table *entry; + + /* special case: no directory and empty directory */ + if (!table || !table->procname) + return 1; + + for (entry = table; entry->procname; entry++) { + if (entry->child) + nr_subheaders += count_subheaders(entry->child); + else + has_files = 1; + } + return nr_subheaders + has_files; +} + +static int register_leaf_sysctl_tables(const char *path, char *pos, + struct ctl_table_header ***subheader, + struct ctl_table_root *root, struct nsproxy *namespaces, + struct ctl_table *table) +{ + struct ctl_table *ctl_table_arg = NULL; + struct ctl_table *entry, *files; + int nr_files = 0; + int nr_dirs = 0; + int err = -ENOMEM; + + for (entry = table; entry->procname; entry++) { + if (entry->child) + nr_dirs++; + else + nr_files++; + } + + files = table; + /* If there are mixed files and directories we need a new table */ + if (nr_dirs && nr_files) { + struct ctl_table *new; + files = kzalloc(sizeof(struct ctl_table) * (nr_files + 1), + GFP_KERNEL); + if (!files) + goto out; + + ctl_table_arg = files; + for (new = files, entry = table; entry->procname; entry++) { + if (entry->child) + continue; + *new = *entry; + new++; + } + } + + /* Register everything except a directory full of subdirectories */ + if (nr_files || !nr_dirs) { + struct ctl_table_header *header; + header = __register_sysctl_table(root, namespaces, path, files); + if (!header) { + kfree(ctl_table_arg); + goto out; + } + + /* Remember if we need to free the file table */ + header->ctl_table_arg = ctl_table_arg; + **subheader = header; + (*subheader)++; + } + + /* Recurse into the subdirectories. */ + for (entry = table; entry->procname; entry++) { + char *child_pos; + + if (!entry->child) + continue; + + err = -ENAMETOOLONG; + child_pos = append_path(path, pos, entry->procname); + if (!child_pos) + goto out; + + err = register_leaf_sysctl_tables(path, child_pos, subheader, + root, namespaces, entry->child); + pos[0] = '\0'; + if (err) + goto out; + } + err = 0; +out: + /* On failure our caller will unregister all registered subheaders */ + return err; +} + /** * __register_sysctl_paths - register a sysctl table hierarchy * @root: List of sysctl headers to register on @@ -1077,7 +1161,8 @@ struct ctl_table_header *__register_sysctl_paths( const struct ctl_path *path, struct ctl_table *table) { struct ctl_table *ctl_table_arg = table; - struct ctl_table_header *header = NULL; + int nr_subheaders = count_subheaders(table); + struct ctl_table_header *header = NULL, **subheaders, **subheader; const struct ctl_path *component; char *new_path, *pos; @@ -1097,12 +1182,39 @@ struct ctl_table_header *__register_sysctl_paths( goto out; table = table->child; } - header = __register_sysctl_table(root, namespaces, new_path, table); - if (header) + if (nr_subheaders == 1) { + header = __register_sysctl_table(root, namespaces, new_path, table); + if (header) + header->ctl_table_arg = ctl_table_arg; + } else { + header = kzalloc(sizeof(*header) + + sizeof(*subheaders)*nr_subheaders, GFP_KERNEL); + if (!header) + goto out; + + subheaders = (struct ctl_table_header **) (header + 1); + subheader = subheaders; header->ctl_table_arg = ctl_table_arg; + + if (register_leaf_sysctl_tables(new_path, pos, &subheader, + root, namespaces, table)) + goto err_register_leaves; + } + out: kfree(new_path); return header; + +err_register_leaves: + while (subheader > subheaders) { + struct ctl_table_header *subh = *(--subheader); + struct ctl_table *table = subh->ctl_table_arg; + unregister_sysctl_table(subh); + kfree(table); + } + kfree(header); + header = NULL; + goto out; } /** @@ -1149,11 +1261,28 @@ EXPORT_SYMBOL(register_sysctl_table); */ void unregister_sysctl_table(struct ctl_table_header * header) { + int nr_subheaders; might_sleep(); if (header == NULL) return; + nr_subheaders = count_subheaders(header->ctl_table_arg); + if (unlikely(nr_subheaders > 1)) { + struct ctl_table_header **subheaders; + int i; + + subheaders = (struct ctl_table_header **)(header + 1); + for (i = nr_subheaders -1; i >= 0; i--) { + struct ctl_table_header *subh = subheaders[i]; + struct ctl_table *table = subh->ctl_table_arg; + unregister_sysctl_table(subh); + kfree(table); + } + kfree(header); + return; + } + spin_lock(&sysctl_lock); start_unregistering(header); if (!--header->parent->count) { diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index a514e0f6056d..25c7dfe1a7ec 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -1015,7 +1015,7 @@ struct ctl_table void *data; int maxlen; umode_t mode; - struct ctl_table *child; + struct ctl_table *child; /* Deprecated */ struct ctl_table *parent; /* Automatically set */ proc_handler *proc_handler; /* Callback for text formatting */ struct ctl_table_poll *poll; -- cgit v1.2.3 From 8d6ecfcc014332fd2fe933f64194160f0e3a6696 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Fri, 6 Jan 2012 11:55:30 -0800 Subject: sysctl: Remove the now unused ctl_table parent field. While useful at one time for selinux and the sysctl sanity checks those users no longer use the parent field and we can safely remove it. Inspired-by: Lucian Adrian Grijincu Signed-off-by: Eric W. Biederman --- fs/proc/proc_sysctl.c | 12 +----------- include/linux/sysctl.h | 1 - 2 files changed, 1 insertion(+), 12 deletions(-) (limited to 'include/linux') diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c index a492ff60e071..e573f9b4f22e 100644 --- a/fs/proc/proc_sysctl.c +++ b/fs/proc/proc_sysctl.c @@ -218,16 +218,6 @@ static int sysctl_perm(struct ctl_table_root *root, struct ctl_table *table, int return test_perm(mode, op); } -static void sysctl_set_parent(struct ctl_table *parent, struct ctl_table *table) -{ - for (; table->procname; table++) { - table->parent = parent; - if (table->child) - sysctl_set_parent(table, table->child); - } -} - - static struct inode *proc_sys_make_inode(struct super_block *sb, struct ctl_table_header *head, struct ctl_table *table) { @@ -947,10 +937,10 @@ struct ctl_table_header *__register_sysctl_table( header->used = 0; header->unregistering = NULL; header->root = root; - sysctl_set_parent(NULL, header->ctl_table); header->count = 1; if (sysctl_check_table(path, table)) goto fail; + spin_lock(&sysctl_lock); header->set = lookup_header_set(root, namespaces); header->attached_by = header->ctl_table; diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index 25c7dfe1a7ec..094bc5ccf1e2 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -1016,7 +1016,6 @@ struct ctl_table int maxlen; umode_t mode; struct ctl_table *child; /* Deprecated */ - struct ctl_table *parent; /* Automatically set */ proc_handler *proc_handler; /* Callback for text formatting */ struct ctl_table_poll *poll; void *extra1; -- cgit v1.2.3 From 938aaa4f9249aa1519fd0db07fc72125de2df338 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Mon, 9 Jan 2012 17:24:30 -0800 Subject: sysctl: Initial support for auto-unregistering sysctl tables. Add nreg to ctl_table_header. When nreg drops to 0 the ctl_table_header will be unregistered. Factor out drop_sysctl_table from unregister_sysctl_table, and add the logic for decrementing nreg. Signed-off-by: Eric W. Biederman --- fs/proc/proc_sysctl.c | 28 +++++++++++++++++++--------- include/linux/sysctl.h | 1 + 2 files changed, 20 insertions(+), 9 deletions(-) (limited to 'include/linux') diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c index 15444850b3e8..13faa48c467e 100644 --- a/fs/proc/proc_sysctl.c +++ b/fs/proc/proc_sysctl.c @@ -29,8 +29,9 @@ static struct ctl_table root_table[1]; static struct ctl_table_root sysctl_table_root; static struct ctl_table_header root_table_header = { {{.count = 1, - .ctl_table = root_table, - .ctl_entry = LIST_HEAD_INIT(sysctl_table_root.default_set.list),}}, + .nreg = 1, + .ctl_table = root_table, + .ctl_entry = LIST_HEAD_INIT(sysctl_table_root.default_set.list),}}, .root = &sysctl_table_root, .set = &sysctl_table_root.default_set, }; @@ -938,6 +939,7 @@ struct ctl_table_header *__register_sysctl_table( header->unregistering = NULL; header->root = root; header->count = 1; + header->nreg = 1; if (sysctl_check_table(path, table)) goto fail; @@ -1192,6 +1194,20 @@ struct ctl_table_header *register_sysctl_table(struct ctl_table *table) } EXPORT_SYMBOL(register_sysctl_table); +static void drop_sysctl_table(struct ctl_table_header *header) +{ + if (--header->nreg) + return; + + start_unregistering(header); + if (!--header->parent->count) { + WARN_ON(1); + kfree_rcu(header->parent, rcu); + } + if (!--header->count) + kfree_rcu(header, rcu); +} + /** * unregister_sysctl_table - unregister a sysctl table hierarchy * @header: the header returned from register_sysctl_table @@ -1224,13 +1240,7 @@ void unregister_sysctl_table(struct ctl_table_header * header) } spin_lock(&sysctl_lock); - start_unregistering(header); - if (!--header->parent->count) { - WARN_ON(1); - kfree_rcu(header->parent, rcu); - } - if (!--header->count) - kfree_rcu(header, rcu); + drop_sysctl_table(header); spin_unlock(&sysctl_lock); } EXPORT_SYMBOL(unregister_sysctl_table); diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index 094bc5ccf1e2..e40b8f6e5d0e 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -1032,6 +1032,7 @@ struct ctl_table_header struct list_head ctl_entry; int used; int count; + int nreg; }; struct rcu_head rcu; }; -- cgit v1.2.3 From 9eb47c26f09e27506d343ef52e634b2a50ee21ef Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Sun, 22 Jan 2012 21:26:00 -0800 Subject: sysctl: Add a root pointer to ctl_table_set Add a ctl_table_root pointer to ctl_table set so it is easy to go from a ctl_table_set to a ctl_table_root. Signed-off-by: Eric W. Biederman --- fs/proc/proc_sysctl.c | 3 +++ include/linux/sysctl.h | 3 +++ net/sysctl_net.c | 5 ++--- 3 files changed, 8 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c index d9c3ae6afe4c..65c13dddceae 100644 --- a/fs/proc/proc_sysctl.c +++ b/fs/proc/proc_sysctl.c @@ -45,6 +45,7 @@ static struct ctl_table_header root_table_header = { static struct ctl_table_root sysctl_table_root = { .root_list = LIST_HEAD_INIT(sysctl_table_root.root_list), .default_set.list = LIST_HEAD_INIT(root_table_header.ctl_entry), + .default_set.root = &sysctl_table_root, }; static DEFINE_SPINLOCK(sysctl_lock); @@ -1348,9 +1349,11 @@ void unregister_sysctl_table(struct ctl_table_header * header) EXPORT_SYMBOL(unregister_sysctl_table); void setup_sysctl_set(struct ctl_table_set *p, + struct ctl_table_root *root, int (*is_seen)(struct ctl_table_set *)) { INIT_LIST_HEAD(&p->list); + p->root = root; p->is_seen = is_seen; } diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index e40b8f6e5d0e..e73ba33cbf08 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -1047,6 +1047,7 @@ struct ctl_table_header struct ctl_table_set { struct list_head list; + struct ctl_table_root *root; int (*is_seen)(struct ctl_table_set *); }; @@ -1069,6 +1070,7 @@ struct ctl_path { void proc_sys_poll_notify(struct ctl_table_poll *poll); extern void setup_sysctl_set(struct ctl_table_set *p, + struct ctl_table_root *root, int (*is_seen)(struct ctl_table_set *)); extern void retire_sysctl_set(struct ctl_table_set *set); @@ -1103,6 +1105,7 @@ static inline void unregister_sysctl_table(struct ctl_table_header * table) } static inline void setup_sysctl_set(struct ctl_table_set *p, + struct ctl_table_root *root, int (*is_seen)(struct ctl_table_set *)) { } diff --git a/net/sysctl_net.c b/net/sysctl_net.c index 07c6b879c8b2..e998c6448046 100644 --- a/net/sysctl_net.c +++ b/net/sysctl_net.c @@ -74,8 +74,7 @@ static struct ctl_table_root net_sysctl_ro_root = { static int __net_init sysctl_net_init(struct net *net) { - setup_sysctl_set(&net->sysctls, - is_seen); + setup_sysctl_set(&net->sysctls, &net_sysctl_root, is_seen); return 0; } @@ -95,7 +94,7 @@ static __init int net_sysctl_init(void) ret = register_pernet_subsys(&sysctl_pernet_ops); if (ret) goto out; - setup_sysctl_set(&net_sysctl_ro_root.default_set, NULL); + setup_sysctl_set(&net_sysctl_ro_root.default_set, &net_sysctl_ro_root, NULL); register_sysctl_root(&net_sysctl_ro_root); register_sysctl_root(&net_sysctl_root); out: -- cgit v1.2.3 From 7ec66d06362da7684a4948c4c2bf1f8546425df4 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 29 Dec 2011 08:24:29 -0800 Subject: sysctl: Stop requiring explicit management of sysctl directories Simplify the code and the sysctl semantics by autogenerating sysctl directories when a sysctl table is registered that needs the directories and autodeleting the directories when there are no more sysctl tables registered that need them. Autogenerating directories keeps sysctl tables from depending on each other, removing all of the arcane register/unregister ordering constraints and makes it impossible to get the order wrong when reigsering and unregistering sysctl tables. Autogenerating directories yields one unique entity that dentries can point to, retaining the current effective use of the dcache. Add struct ctl_dir as the type of these new autogenerated directories. The attached_by and attached_to fields in ctl_table_header are removed as they are no longer needed. The child field in ctl_table is no longer needed by the core of the sysctl code. ctl_table.child can be removed once all of the existing users have been updated. Benchmark before: make-dummies 0 999 -> 0.7s rmmod dummy -> 0.07s make-dummies 0 9999 -> 1m10s rmmod dummy -> 0.4s Benchmark after: make-dummies 0 999 -> 0.44s rmmod dummy -> 0.065s make-dummies 0 9999 -> 1m36s rmmod dummy -> 0.4s Signed-off-by: Eric W. Biederman --- fs/proc/proc_sysctl.c | 342 +++++++++++++++++++++---------------------------- include/linux/sysctl.h | 10 +- 2 files changed, 150 insertions(+), 202 deletions(-) (limited to 'include/linux') diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c index 65c13dddceae..3c0767d5a55f 100644 --- a/fs/proc/proc_sysctl.c +++ b/fs/proc/proc_sysctl.c @@ -28,28 +28,31 @@ void proc_sys_poll_notify(struct ctl_table_poll *poll) static struct ctl_table root_table[] = { { .procname = "", - .mode = S_IRUGO|S_IXUGO, - .child = &root_table[1], + .mode = S_IFDIR|S_IRUGO|S_IXUGO, }, { } }; static struct ctl_table_root sysctl_table_root; -static struct ctl_table_header root_table_header = { - {{.count = 1, - .nreg = 1, - .ctl_table = root_table, - .ctl_entry = LIST_HEAD_INIT(sysctl_table_root.default_set.list),}}, - .root = &sysctl_table_root, - .set = &sysctl_table_root.default_set, +static struct ctl_dir sysctl_root_dir = { + .header = { + {{.count = 1, + .nreg = 1, + .ctl_table = root_table, + .ctl_entry = LIST_HEAD_INIT(sysctl_table_root.default_set.list),}}, + .root = &sysctl_table_root, + .set = &sysctl_table_root.default_set, + }, }; static struct ctl_table_root sysctl_table_root = { .root_list = LIST_HEAD_INIT(sysctl_table_root.root_list), - .default_set.list = LIST_HEAD_INIT(root_table_header.ctl_entry), + .default_set.list = LIST_HEAD_INIT(sysctl_root_dir.header.ctl_entry), .default_set.root = &sysctl_table_root, }; static DEFINE_SPINLOCK(sysctl_lock); +static void drop_sysctl_table(struct ctl_table_header *header); + static int namecmp(const char *name1, int len1, const char *name2, int len2) { int minlen; @@ -66,29 +69,18 @@ static int namecmp(const char *name1, int len1, const char *name2, int len2) } static struct ctl_table *find_entry(struct ctl_table_header **phead, - struct ctl_table_set *set, - struct ctl_table_header *dir_head, struct ctl_table *dir, + struct ctl_table_set *set, struct ctl_dir *dir, const char *name, int namelen) { struct ctl_table_header *head; struct ctl_table *entry; - if (dir_head->set == set) { - for (entry = dir; entry->procname; entry++) { - const char *procname = entry->procname; - if (namecmp(procname, strlen(procname), name, namelen) == 0) { - *phead = dir_head; - return entry; - } - } - } - list_for_each_entry(head, &set->list, ctl_entry) { if (head->unregistering) continue; - if (head->attached_to != dir) + if (head->parent != dir) continue; - for (entry = head->attached_by; entry->procname; entry++) { + for (entry = head->ctl_table; entry->procname; entry++) { const char *procname = entry->procname; if (namecmp(procname, strlen(procname), name, namelen) == 0) { *phead = head; @@ -103,6 +95,7 @@ static void init_header(struct ctl_table_header *head, struct ctl_table_root *root, struct ctl_table_set *set, struct ctl_table *table) { + head->ctl_table = table; head->ctl_table_arg = table; INIT_LIST_HEAD(&head->ctl_entry); head->used = 0; @@ -119,9 +112,10 @@ static void erase_header(struct ctl_table_header *head) list_del_init(&head->ctl_entry); } -static void insert_header(struct ctl_table_header *header) +static void insert_header(struct ctl_dir *dir, struct ctl_table_header *header) { - header->parent->count++; + header->parent = dir; + header->parent->header.nreg++; list_add_tail(&header->ctl_entry, &header->set->list); } @@ -219,8 +213,7 @@ lookup_header_list(struct ctl_table_root *root, struct nsproxy *namespaces) } static struct ctl_table *lookup_entry(struct ctl_table_header **phead, - struct ctl_table_header *dir_head, - struct ctl_table *dir, + struct ctl_dir *dir, const char *name, int namelen) { struct ctl_table_header *head; @@ -232,7 +225,7 @@ static struct ctl_table *lookup_entry(struct ctl_table_header **phead, root = &sysctl_table_root; do { set = lookup_header_set(root, current->nsproxy); - entry = find_entry(&head, set, dir_head, dir, name, namelen); + entry = find_entry(&head, set, dir, name, namelen); if (entry && use_table(head)) *phead = head; else @@ -244,7 +237,7 @@ static struct ctl_table *lookup_entry(struct ctl_table_header **phead, return entry; } -static struct ctl_table_header *next_usable_entry(struct ctl_table *dir, +static struct ctl_table_header *next_usable_entry(struct ctl_dir *dir, struct ctl_table_root *root, struct list_head *tmp) { struct nsproxy *namespaces = current->nsproxy; @@ -256,8 +249,8 @@ static struct ctl_table_header *next_usable_entry(struct ctl_table *dir, head = list_entry(tmp, struct ctl_table_header, ctl_entry); root = head->root; - if (head->attached_to != dir || - !head->attached_by->procname || + if (head->parent != dir || + !head->ctl_table->procname || !use_table(head)) goto next; @@ -281,47 +274,35 @@ out: return NULL; } -static void first_entry( - struct ctl_table_header *dir_head, struct ctl_table *dir, +static void first_entry(struct ctl_dir *dir, struct ctl_table_header **phead, struct ctl_table **pentry) { - struct ctl_table_header *head = dir_head; - struct ctl_table *entry = dir; + struct ctl_table_header *head; + struct ctl_table *entry = NULL; spin_lock(&sysctl_lock); - if (entry->procname) { - use_table(head); - } else { - head = next_usable_entry(dir, &sysctl_table_root, - &sysctl_table_root.default_set.list); - if (head) - entry = head->attached_by; - } + head = next_usable_entry(dir, &sysctl_table_root, + &sysctl_table_root.default_set.list); spin_unlock(&sysctl_lock); + if (head) + entry = head->ctl_table; *phead = head; *pentry = entry; } -static void next_entry(struct ctl_table *dir, - struct ctl_table_header **phead, struct ctl_table **pentry) +static void next_entry(struct ctl_table_header **phead, struct ctl_table **pentry) { struct ctl_table_header *head = *phead; struct ctl_table *entry = *pentry; entry++; if (!entry->procname) { - struct ctl_table_root *root = head->root; - struct list_head *tmp = &head->ctl_entry; - if (head->attached_to != dir) { - root = &sysctl_table_root; - tmp = &sysctl_table_root.default_set.list; - } spin_lock(&sysctl_lock); unuse_table(head); - head = next_usable_entry(dir, root, tmp); + head = next_usable_entry(head->parent, head->root, &head->ctl_entry); spin_unlock(&sysctl_lock); if (head) - entry = head->attached_by; + entry = head->ctl_table; } *phead = head; *pentry = entry; @@ -381,7 +362,7 @@ static struct inode *proc_sys_make_inode(struct super_block *sb, inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; inode->i_mode = table->mode; - if (!table->child) { + if (!S_ISDIR(table->mode)) { inode->i_mode |= S_IFREG; inode->i_op = &proc_sys_inode_operations; inode->i_fop = &proc_sys_file_operations; @@ -398,7 +379,7 @@ static struct ctl_table_header *grab_header(struct inode *inode) { struct ctl_table_header *head = PROC_I(inode)->sysctl; if (!head) - head = &root_table_header; + head = &sysctl_root_dir.header; return sysctl_head_grab(head); } @@ -406,24 +387,19 @@ static struct dentry *proc_sys_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) { struct ctl_table_header *head = grab_header(dir); - struct ctl_table *table = PROC_I(dir)->sysctl_entry; struct ctl_table_header *h = NULL; struct qstr *name = &dentry->d_name; struct ctl_table *p; struct inode *inode; struct dentry *err = ERR_PTR(-ENOENT); + struct ctl_dir *ctl_dir; if (IS_ERR(head)) return ERR_CAST(head); - if (table && !table->child) { - WARN_ON(1); - goto out; - } + ctl_dir = container_of(head, struct ctl_dir, header); - table = table ? table->child : &head->ctl_table[1]; - - p = lookup_entry(&h, head, table, name->name, name->len); + p = lookup_entry(&h, ctl_dir, name->name, name->len); if (!p) goto out; @@ -586,21 +562,16 @@ static int proc_sys_readdir(struct file *filp, void *dirent, filldir_t filldir) struct dentry *dentry = filp->f_path.dentry; struct inode *inode = dentry->d_inode; struct ctl_table_header *head = grab_header(inode); - struct ctl_table *table = PROC_I(inode)->sysctl_entry; struct ctl_table_header *h = NULL; struct ctl_table *entry; + struct ctl_dir *ctl_dir; unsigned long pos; int ret = -EINVAL; if (IS_ERR(head)) return PTR_ERR(head); - if (table && !table->child) { - WARN_ON(1); - goto out; - } - - table = table ? table->child : &head->ctl_table[1]; + ctl_dir = container_of(head, struct ctl_dir, header); ret = 0; /* Avoid a switch here: arm builds fail with missing __cmpdi2 */ @@ -618,7 +589,7 @@ static int proc_sys_readdir(struct file *filp, void *dirent, filldir_t filldir) } pos = 2; - for (first_entry(head, table, &h, &entry); h; next_entry(table, &h, &entry)) { + for (first_entry(ctl_dir, &h, &entry); h; next_entry(&h, &entry)) { ret = scan(h, entry, &pos, filp, dirent, filldir); if (ret) { sysctl_head_finish(h); @@ -779,52 +750,86 @@ static const struct dentry_operations proc_sys_dentry_operations = { .d_compare = proc_sys_compare, }; -static struct ctl_table *is_branch_in(struct ctl_table *branch, - struct ctl_table *table) +static struct ctl_dir *find_subdir(struct ctl_table_set *set, struct ctl_dir *dir, + const char *name, int namelen) { - struct ctl_table *p; - const char *s = branch->procname; + struct ctl_table_header *head; + struct ctl_table *entry; - /* branch should have named subdirectory as its first element */ - if (!s || !branch->child) - return NULL; + entry = find_entry(&head, set, dir, name, namelen); + if (!entry) + return ERR_PTR(-ENOENT); + if (S_ISDIR(entry->mode)) + return container_of(head, struct ctl_dir, header); + return ERR_PTR(-ENOTDIR); +} + +static struct ctl_dir *new_dir(struct ctl_table_set *set, + const char *name, int namelen) +{ + struct ctl_table *table; + struct ctl_dir *new; + char *new_name; - /* ... and nothing else */ - if (branch[1].procname) + new = kzalloc(sizeof(*new) + sizeof(struct ctl_table)*2 + + namelen + 1, GFP_KERNEL); + if (!new) return NULL; - /* table should contain subdirectory with the same name */ - for (p = table; p->procname; p++) { - if (!p->child) - continue; - if (p->procname && strcmp(p->procname, s) == 0) - return p; - } - return NULL; + table = (struct ctl_table *)(new + 1); + new_name = (char *)(table + 2); + memcpy(new_name, name, namelen); + new_name[namelen] = '\0'; + table[0].procname = new_name; + table[0].mode = S_IFDIR|S_IRUGO|S_IXUGO; + init_header(&new->header, set->root, set, table); + + return new; } -/* see if attaching q to p would be an improvement */ -static void try_attach(struct ctl_table_header *p, struct ctl_table_header *q) +static struct ctl_dir *get_subdir(struct ctl_table_set *set, + struct ctl_dir *dir, const char *name, int namelen) { - struct ctl_table *to = p->ctl_table, *by = q->ctl_table; - struct ctl_table *next; - int is_better = 0; - int not_in_parent = !p->attached_by; - - while ((next = is_branch_in(by, to)) != NULL) { - if (by == q->attached_by) - is_better = 1; - if (to == p->attached_by) - not_in_parent = 1; - by = by->child; - to = next->child; - } + struct ctl_dir *subdir, *new = NULL; - if (is_better && not_in_parent) { - q->attached_by = by; - q->attached_to = to; - q->parent = p; + spin_lock(&sysctl_lock); + subdir = find_subdir(dir->header.set, dir, name, namelen); + if (!IS_ERR(subdir)) + goto found; + if ((PTR_ERR(subdir) == -ENOENT) && set != dir->header.set) + subdir = find_subdir(set, dir, name, namelen); + if (!IS_ERR(subdir)) + goto found; + if (PTR_ERR(subdir) != -ENOENT) + goto failed; + + spin_unlock(&sysctl_lock); + new = new_dir(set, name, namelen); + spin_lock(&sysctl_lock); + subdir = ERR_PTR(-ENOMEM); + if (!new) + goto failed; + + subdir = find_subdir(set, dir, name, namelen); + if (!IS_ERR(subdir)) + goto found; + if (PTR_ERR(subdir) != -ENOENT) + goto failed; + + insert_header(dir, &new->header); + subdir = new; +found: + subdir->header.nreg++; +failed: + if (unlikely(IS_ERR(subdir))) { + printk(KERN_ERR "sysctl could not get directory: %*.*s %ld\n", + namelen, namelen, name, PTR_ERR(subdir)); } + drop_sysctl_table(&dir->header); + if (new) + drop_sysctl_table(&new->header); + spin_unlock(&sysctl_lock); + return subdir; } static int sysctl_check_table_dups(const char *path, struct ctl_table *old, @@ -846,24 +851,14 @@ static int sysctl_check_table_dups(const char *path, struct ctl_table *old, } static int sysctl_check_dups(struct nsproxy *namespaces, - struct ctl_table_header *header, + struct ctl_dir *dir, const char *path, struct ctl_table *table) { struct ctl_table_root *root; struct ctl_table_set *set; - struct ctl_table_header *dir_head, *head; - struct ctl_table *dir_table; + struct ctl_table_header *head; int error = 0; - /* No dups if we are the only member of our directory */ - if (header->attached_by != table) - return 0; - - dir_head = header->parent; - dir_table = header->attached_to; - - error = sysctl_check_table_dups(path, dir_table, table); - root = &sysctl_table_root; do { set = lookup_header_set(root, namespaces); @@ -871,9 +866,9 @@ static int sysctl_check_dups(struct nsproxy *namespaces, list_for_each_entry(head, &set->list, ctl_entry) { if (head->unregistering) continue; - if (head->attached_to != dir_table) + if (head->parent != dir) continue; - error = sysctl_check_table_dups(path, head->attached_by, + error = sysctl_check_table_dups(path, head->ctl_table, table); } root = list_entry(root->root_list.next, @@ -977,47 +972,25 @@ struct ctl_table_header *__register_sysctl_table( const char *path, struct ctl_table *table) { struct ctl_table_header *header; - struct ctl_table *new, **prevp; const char *name, *nextname; - unsigned int npath = 0; struct ctl_table_set *set; - size_t path_bytes = 0; - char *new_name; - - /* Count the path components */ - for (name = path; name; name = nextname) { - int namelen; - nextname = strchr(name, '/'); - if (nextname) { - namelen = nextname - name; - nextname++; - } else { - namelen = strlen(name); - } - if (namelen == 0) - continue; - path_bytes += namelen + 1; - npath++; - } + struct ctl_dir *dir; - /* - * For each path component, allocate a 2-element ctl_table array. - * The first array element will be filled with the sysctl entry - * for this, the second will be the sentinel (procname == 0). - * - * We allocate everything in one go so that we don't have to - * worry about freeing additional memory in unregister_sysctl_table. - */ - header = kzalloc(sizeof(struct ctl_table_header) + path_bytes + - (2 * npath * sizeof(struct ctl_table)), GFP_KERNEL); + header = kzalloc(sizeof(struct ctl_table_header), GFP_KERNEL); if (!header) return NULL; - new = (struct ctl_table *) (header + 1); - new_name = (char *)(new + (2 * npath)); + init_header(header, root, NULL, table); + if (sysctl_check_table(path, table)) + goto fail; + + spin_lock(&sysctl_lock); + header->set = set = lookup_header_set(root, namespaces); + dir = &sysctl_root_dir; + dir->header.nreg++; + spin_unlock(&sysctl_lock); - /* Now connect the dots */ - prevp = &header->ctl_table; + /* Find the directory for the ctl_table */ for (name = path; name; name = nextname) { int namelen; nextname = strchr(name, '/'); @@ -1029,51 +1002,21 @@ struct ctl_table_header *__register_sysctl_table( } if (namelen == 0) continue; - memcpy(new_name, name, namelen); - new_name[namelen] = '\0'; - - new->procname = new_name; - new->mode = 0555; - - *prevp = new; - prevp = &new->child; - new += 2; - new_name += namelen + 1; + dir = get_subdir(set, dir, name, namelen); + if (IS_ERR(dir)) + goto fail; } - *prevp = table; - - init_header(header, root, NULL, table); - if (sysctl_check_table(path, table)) - goto fail; - spin_lock(&sysctl_lock); - header->set = lookup_header_set(root, namespaces); - header->attached_by = header->ctl_table; - header->attached_to = &root_table[1]; - header->parent = &root_table_header; - set = header->set; - root = header->root; - for (;;) { - struct ctl_table_header *p; - list_for_each_entry(p, &set->list, ctl_entry) { - if (p->unregistering) - continue; - try_attach(p, header); - } - if (root == &sysctl_table_root) - break; - root = list_entry(root->root_list.prev, - struct ctl_table_root, root_list); - set = lookup_header_set(root, namespaces); - } - if (sysctl_check_dups(namespaces, header, path, table)) - goto fail_locked; - insert_header(header); + if (sysctl_check_dups(namespaces, dir, path, table)) + goto fail_put_dir_locked; + insert_header(dir, header); + drop_sysctl_table(&dir->header); spin_unlock(&sysctl_lock); return header; -fail_locked: +fail_put_dir_locked: + drop_sysctl_table(&dir->header); spin_unlock(&sysctl_lock); fail: kfree(header); @@ -1299,16 +1242,17 @@ EXPORT_SYMBOL(register_sysctl_table); static void drop_sysctl_table(struct ctl_table_header *header) { + struct ctl_dir *parent = header->parent; + if (--header->nreg) return; start_unregistering(header); - if (!--header->parent->count) { - WARN_ON(1); - kfree_rcu(header->parent, rcu); - } if (!--header->count) kfree_rcu(header, rcu); + + if (parent) + drop_sysctl_table(&parent->header); } /** diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index e73ba33cbf08..3084b624868c 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -938,6 +938,7 @@ struct ctl_table; struct nsproxy; struct ctl_table_root; struct ctl_table_header; +struct ctl_dir; typedef struct ctl_table ctl_table; @@ -1040,9 +1041,12 @@ struct ctl_table_header struct ctl_table *ctl_table_arg; struct ctl_table_root *root; struct ctl_table_set *set; - struct ctl_table *attached_by; - struct ctl_table *attached_to; - struct ctl_table_header *parent; + struct ctl_dir *parent; +}; + +struct ctl_dir { + /* Header must be at the start of ctl_dir */ + struct ctl_table_header header; }; struct ctl_table_set { -- cgit v1.2.3 From 0e47c99d7fe25e0f3907d9f3401079169d904891 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Sat, 7 Jan 2012 23:24:30 -0800 Subject: sysctl: Replace root_list with links between sysctl_table_sets. Piecing together directories by looking first in one directory tree, than in another directory tree and finally in a third directory tree makes it hard to verify that some directory entries are not multiply defined and makes it hard to create efficient implementations the sysctl filesystem. Replace the sysctl wide list of roots with autogenerated links from the core sysctl directory tree to the other sysctl directory trees. This simplifies sysctl directory reading and lookups as now only entries in a single sysctl directory tree need to be considered. Benchmark before: make-dummies 0 999 -> 0.44s rmmod dummy -> 0.065s make-dummies 0 9999 -> 1m36s rmmod dummy -> 0.4s Benchmark after: make-dummies 0 999 -> 0.63s rmmod dummy -> 0.12s make-dummies 0 9999 -> 2m35s rmmod dummy -> 18s The slowdown is caused by the lookups used in insert_headers and put_links to see if we need to add links or remove links. Signed-off-by: Eric W. Biederman --- fs/proc/proc_sysctl.c | 397 ++++++++++++++++++++++++++++++++++++------------- include/linux/sysctl.h | 3 +- 2 files changed, 296 insertions(+), 104 deletions(-) (limited to 'include/linux') diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c index a78556514a87..ec54a57c4690 100644 --- a/fs/proc/proc_sysctl.c +++ b/fs/proc/proc_sysctl.c @@ -32,26 +32,26 @@ static struct ctl_table root_table[] = { }, { } }; -static struct ctl_table_root sysctl_table_root; -static struct ctl_dir sysctl_root_dir = { - .header = { +static struct ctl_table_root sysctl_table_root = { + .default_set.list = LIST_HEAD_INIT(sysctl_table_root.default_set.dir.header.ctl_entry), + .default_set.dir.header = { {{.count = 1, .nreg = 1, .ctl_table = root_table, .ctl_entry = LIST_HEAD_INIT(sysctl_table_root.default_set.list),}}, + .ctl_table_arg = root_table, .root = &sysctl_table_root, .set = &sysctl_table_root.default_set, }, }; -static struct ctl_table_root sysctl_table_root = { - .root_list = LIST_HEAD_INIT(sysctl_table_root.root_list), - .default_set.list = LIST_HEAD_INIT(sysctl_root_dir.header.ctl_entry), - .default_set.root = &sysctl_table_root, -}; static DEFINE_SPINLOCK(sysctl_lock); static void drop_sysctl_table(struct ctl_table_header *header); +static int sysctl_follow_link(struct ctl_table_header **phead, + struct ctl_table **pentry, struct nsproxy *namespaces); +static int insert_links(struct ctl_table_header *head); +static void put_links(struct ctl_table_header *header); static void sysctl_print_dir(struct ctl_dir *dir) { @@ -76,9 +76,9 @@ static int namecmp(const char *name1, int len1, const char *name2, int len2) } static struct ctl_table *find_entry(struct ctl_table_header **phead, - struct ctl_table_set *set, struct ctl_dir *dir, - const char *name, int namelen) + struct ctl_dir *dir, const char *name, int namelen) { + struct ctl_table_set *set = dir->header.set; struct ctl_table_header *head; struct ctl_table *entry; @@ -119,11 +119,21 @@ static void erase_header(struct ctl_table_header *head) list_del_init(&head->ctl_entry); } -static void insert_header(struct ctl_dir *dir, struct ctl_table_header *header) +static int insert_header(struct ctl_dir *dir, struct ctl_table_header *header) { + int err; + + dir->header.nreg++; header->parent = dir; - header->parent->header.nreg++; + err = insert_links(header); + if (err) + goto fail_links; list_add_tail(&header->ctl_entry, &header->set->list); + return 0; +fail_links: + header->parent = NULL; + drop_sysctl_table(&dir->header); + return err; } /* called under sysctl_lock */ @@ -212,72 +222,39 @@ lookup_header_set(struct ctl_table_root *root, struct nsproxy *namespaces) return set; } -static struct list_head * -lookup_header_list(struct ctl_table_root *root, struct nsproxy *namespaces) -{ - struct ctl_table_set *set = lookup_header_set(root, namespaces); - return &set->list; -} - static struct ctl_table *lookup_entry(struct ctl_table_header **phead, struct ctl_dir *dir, const char *name, int namelen) { struct ctl_table_header *head; struct ctl_table *entry; - struct ctl_table_root *root; - struct ctl_table_set *set; spin_lock(&sysctl_lock); - root = &sysctl_table_root; - do { - set = lookup_header_set(root, current->nsproxy); - entry = find_entry(&head, set, dir, name, namelen); - if (entry && use_table(head)) - *phead = head; - else - entry = NULL; - root = list_entry(root->root_list.next, - struct ctl_table_root, root_list); - } while (!entry && root != &sysctl_table_root); + entry = find_entry(&head, dir, name, namelen); + if (entry && use_table(head)) + *phead = head; + else + entry = NULL; spin_unlock(&sysctl_lock); return entry; } static struct ctl_table_header *next_usable_entry(struct ctl_dir *dir, - struct ctl_table_root *root, struct list_head *tmp) + struct list_head *tmp) { - struct nsproxy *namespaces = current->nsproxy; - struct list_head *header_list; + struct ctl_table_set *set = dir->header.set; struct ctl_table_header *head; - goto next; - for (;;) { + for (tmp = tmp->next; tmp != &set->list; tmp = tmp->next) { head = list_entry(tmp, struct ctl_table_header, ctl_entry); - root = head->root; if (head->parent != dir || !head->ctl_table->procname || !use_table(head)) - goto next; - - return head; - next: - tmp = tmp->next; - header_list = lookup_header_list(root, namespaces); - if (tmp != header_list) continue; - do { - root = list_entry(root->root_list.next, - struct ctl_table_root, root_list); - if (root == &sysctl_table_root) - goto out; - header_list = lookup_header_list(root, namespaces); - } while (list_empty(header_list)); - tmp = header_list->next; + return head; } -out: return NULL; } @@ -288,8 +265,7 @@ static void first_entry(struct ctl_dir *dir, struct ctl_table *entry = NULL; spin_lock(&sysctl_lock); - head = next_usable_entry(dir, &sysctl_table_root, - &sysctl_table_root.default_set.list); + head = next_usable_entry(dir, &dir->header.set->list); spin_unlock(&sysctl_lock); if (head) entry = head->ctl_table; @@ -306,7 +282,7 @@ static void next_entry(struct ctl_table_header **phead, struct ctl_table **pentr if (!entry->procname) { spin_lock(&sysctl_lock); unuse_table(head); - head = next_usable_entry(head->parent, head->root, &head->ctl_entry); + head = next_usable_entry(head->parent, &head->ctl_entry); spin_unlock(&sysctl_lock); if (head) entry = head->ctl_table; @@ -317,9 +293,6 @@ static void next_entry(struct ctl_table_header **phead, struct ctl_table **pentr void register_sysctl_root(struct ctl_table_root *root) { - spin_lock(&sysctl_lock); - list_add_tail(&root->root_list, &sysctl_table_root.root_list); - spin_unlock(&sysctl_lock); } /* @@ -386,7 +359,7 @@ static struct ctl_table_header *grab_header(struct inode *inode) { struct ctl_table_header *head = PROC_I(inode)->sysctl; if (!head) - head = &sysctl_root_dir.header; + head = &sysctl_table_root.default_set.dir.header; return sysctl_head_grab(head); } @@ -400,6 +373,7 @@ static struct dentry *proc_sys_lookup(struct inode *dir, struct dentry *dentry, struct inode *inode; struct dentry *err = ERR_PTR(-ENOENT); struct ctl_dir *ctl_dir; + int ret; if (IS_ERR(head)) return ERR_CAST(head); @@ -410,6 +384,11 @@ static struct dentry *proc_sys_lookup(struct inode *dir, struct dentry *dentry, if (!p) goto out; + ret = sysctl_follow_link(&h, &p, current->nsproxy); + err = ERR_PTR(ret); + if (ret) + goto out; + err = ERR_PTR(-ENOMEM); inode = proc_sys_make_inode(dir->i_sb, h ? h : head, p); if (h) @@ -547,6 +526,25 @@ static int proc_sys_fill_cache(struct file *filp, void *dirent, return !!filldir(dirent, qname.name, qname.len, filp->f_pos, ino, type); } +static int proc_sys_link_fill_cache(struct file *filp, void *dirent, + filldir_t filldir, + struct ctl_table_header *head, + struct ctl_table *table) +{ + int err, ret = 0; + head = sysctl_head_grab(head); + + /* It is not an error if we can not follow the link ignore it */ + err = sysctl_follow_link(&head, &table, current->nsproxy); + if (err) + goto out; + + ret = proc_sys_fill_cache(filp, dirent, filldir, head, table); +out: + sysctl_head_finish(head); + return ret; +} + static int scan(struct ctl_table_header *head, ctl_table *table, unsigned long *pos, struct file *file, void *dirent, filldir_t filldir) @@ -556,7 +554,10 @@ static int scan(struct ctl_table_header *head, ctl_table *table, if ((*pos)++ < file->f_pos) return 0; - res = proc_sys_fill_cache(file, dirent, filldir, head, table); + if (unlikely(S_ISLNK(table->mode))) + res = proc_sys_link_fill_cache(file, dirent, filldir, head, table); + else + res = proc_sys_fill_cache(file, dirent, filldir, head, table); if (res == 0) file->f_pos = *pos; @@ -757,13 +758,13 @@ static const struct dentry_operations proc_sys_dentry_operations = { .d_compare = proc_sys_compare, }; -static struct ctl_dir *find_subdir(struct ctl_table_set *set, struct ctl_dir *dir, - const char *name, int namelen) +static struct ctl_dir *find_subdir(struct ctl_dir *dir, + const char *name, int namelen) { struct ctl_table_header *head; struct ctl_table *entry; - entry = find_entry(&head, set, dir, name, namelen); + entry = find_entry(&head, dir, name, namelen); if (!entry) return ERR_PTR(-ENOENT); if (S_ISDIR(entry->mode)) @@ -772,7 +773,7 @@ static struct ctl_dir *find_subdir(struct ctl_table_set *set, struct ctl_dir *di } static struct ctl_dir *new_dir(struct ctl_table_set *set, - const char *name, int namelen) + const char *name, int namelen) { struct ctl_table *table; struct ctl_dir *new; @@ -789,22 +790,19 @@ static struct ctl_dir *new_dir(struct ctl_table_set *set, new_name[namelen] = '\0'; table[0].procname = new_name; table[0].mode = S_IFDIR|S_IRUGO|S_IXUGO; - init_header(&new->header, set->root, set, table); + init_header(&new->header, set->dir.header.root, set, table); return new; } -static struct ctl_dir *get_subdir(struct ctl_table_set *set, - struct ctl_dir *dir, const char *name, int namelen) +static struct ctl_dir *get_subdir(struct ctl_dir *dir, + const char *name, int namelen) { + struct ctl_table_set *set = dir->header.set; struct ctl_dir *subdir, *new = NULL; spin_lock(&sysctl_lock); - subdir = find_subdir(dir->header.set, dir, name, namelen); - if (!IS_ERR(subdir)) - goto found; - if ((PTR_ERR(subdir) == -ENOENT) && set != dir->header.set) - subdir = find_subdir(set, dir, name, namelen); + subdir = find_subdir(dir, name, namelen); if (!IS_ERR(subdir)) goto found; if (PTR_ERR(subdir) != -ENOENT) @@ -817,13 +815,14 @@ static struct ctl_dir *get_subdir(struct ctl_table_set *set, if (!new) goto failed; - subdir = find_subdir(set, dir, name, namelen); + subdir = find_subdir(dir, name, namelen); if (!IS_ERR(subdir)) goto found; if (PTR_ERR(subdir) != -ENOENT) goto failed; - insert_header(dir, &new->header); + if (insert_header(dir, &new->header)) + goto failed; subdir = new; found: subdir->header.nreg++; @@ -841,6 +840,57 @@ failed: return subdir; } +static struct ctl_dir *xlate_dir(struct ctl_table_set *set, struct ctl_dir *dir) +{ + struct ctl_dir *parent; + const char *procname; + if (!dir->header.parent) + return &set->dir; + parent = xlate_dir(set, dir->header.parent); + if (IS_ERR(parent)) + return parent; + procname = dir->header.ctl_table[0].procname; + return find_subdir(parent, procname, strlen(procname)); +} + +static int sysctl_follow_link(struct ctl_table_header **phead, + struct ctl_table **pentry, struct nsproxy *namespaces) +{ + struct ctl_table_header *head; + struct ctl_table_root *root; + struct ctl_table_set *set; + struct ctl_table *entry; + struct ctl_dir *dir; + int ret; + + /* Get out quickly if not a link */ + if (!S_ISLNK((*pentry)->mode)) + return 0; + + ret = 0; + spin_lock(&sysctl_lock); + root = (*pentry)->data; + set = lookup_header_set(root, namespaces); + dir = xlate_dir(set, (*phead)->parent); + if (IS_ERR(dir)) + ret = PTR_ERR(dir); + else { + const char *procname = (*pentry)->procname; + head = NULL; + entry = find_entry(&head, dir, procname, strlen(procname)); + ret = -ENOENT; + if (entry && use_table(head)) { + unuse_table(*phead); + *phead = head; + *pentry = entry; + ret = 0; + } + } + + spin_unlock(&sysctl_lock); + return ret; +} + static int sysctl_check_table_dups(const char *path, struct ctl_table *old, struct ctl_table *table) { @@ -859,30 +909,21 @@ static int sysctl_check_table_dups(const char *path, struct ctl_table *old, return error; } -static int sysctl_check_dups(struct nsproxy *namespaces, - struct ctl_dir *dir, +static int sysctl_check_dups(struct ctl_dir *dir, const char *path, struct ctl_table *table) { - struct ctl_table_root *root; struct ctl_table_set *set; struct ctl_table_header *head; int error = 0; - root = &sysctl_table_root; - do { - set = lookup_header_set(root, namespaces); - - list_for_each_entry(head, &set->list, ctl_entry) { - if (head->unregistering) - continue; - if (head->parent != dir) - continue; - error = sysctl_check_table_dups(path, head->ctl_table, - table); - } - root = list_entry(root->root_list.next, - struct ctl_table_root, root_list); - } while (root != &sysctl_table_root); + set = dir->header.set; + list_for_each_entry(head, &set->list, ctl_entry) { + if (head->unregistering) + continue; + if (head->parent != dir) + continue; + error = sysctl_check_table_dups(path, head->ctl_table, table); + } return error; } @@ -932,6 +973,115 @@ static int sysctl_check_table(const char *path, struct ctl_table *table) return err; } +static struct ctl_table_header *new_links(struct ctl_dir *dir, struct ctl_table *table, + struct ctl_table_root *link_root) +{ + struct ctl_table *link_table, *entry, *link; + struct ctl_table_header *links; + char *link_name; + int nr_entries, name_bytes; + + name_bytes = 0; + nr_entries = 0; + for (entry = table; entry->procname; entry++) { + nr_entries++; + name_bytes += strlen(entry->procname) + 1; + } + + links = kzalloc(sizeof(struct ctl_table_header) + + sizeof(struct ctl_table)*(nr_entries + 1) + + name_bytes, + GFP_KERNEL); + + if (!links) + return NULL; + + link_table = (struct ctl_table *)(links + 1); + link_name = (char *)&link_table[nr_entries + 1]; + + for (link = link_table, entry = table; entry->procname; link++, entry++) { + int len = strlen(entry->procname) + 1; + memcpy(link_name, entry->procname, len); + link->procname = link_name; + link->mode = S_IFLNK|S_IRWXUGO; + link->data = link_root; + link_name += len; + } + init_header(links, dir->header.root, dir->header.set, link_table); + links->nreg = nr_entries; + + return links; +} + +static bool get_links(struct ctl_dir *dir, + struct ctl_table *table, struct ctl_table_root *link_root) +{ + struct ctl_table_header *head; + struct ctl_table *entry, *link; + + /* Are there links available for every entry in table? */ + for (entry = table; entry->procname; entry++) { + const char *procname = entry->procname; + link = find_entry(&head, dir, procname, strlen(procname)); + if (!link) + return false; + if (S_ISDIR(link->mode) && S_ISDIR(entry->mode)) + continue; + if (S_ISLNK(link->mode) && (link->data == link_root)) + continue; + return false; + } + + /* The checks passed. Increase the registration count on the links */ + for (entry = table; entry->procname; entry++) { + const char *procname = entry->procname; + link = find_entry(&head, dir, procname, strlen(procname)); + head->nreg++; + } + return true; +} + +static int insert_links(struct ctl_table_header *head) +{ + struct ctl_table_set *root_set = &sysctl_table_root.default_set; + struct ctl_dir *core_parent = NULL; + struct ctl_table_header *links; + int err; + + if (head->set == root_set) + return 0; + + core_parent = xlate_dir(root_set, head->parent); + if (IS_ERR(core_parent)) + return 0; + + if (get_links(core_parent, head->ctl_table, head->root)) + return 0; + + core_parent->header.nreg++; + spin_unlock(&sysctl_lock); + + links = new_links(core_parent, head->ctl_table, head->root); + + spin_lock(&sysctl_lock); + err = -ENOMEM; + if (!links) + goto out; + + err = 0; + if (get_links(core_parent, head->ctl_table, head->root)) { + kfree(links); + goto out; + } + + err = insert_header(core_parent, links); + if (err) + kfree(links); +out: + drop_sysctl_table(&core_parent->header); + return err; +} + /** * __register_sysctl_table - register a leaf sysctl table * @root: List of sysctl headers to register on @@ -980,6 +1130,7 @@ struct ctl_table_header *__register_sysctl_table( struct nsproxy *namespaces, const char *path, struct ctl_table *table) { + struct ctl_table_header *links = NULL; struct ctl_table_header *header; const char *name, *nextname; struct ctl_table_set *set; @@ -995,7 +1146,7 @@ struct ctl_table_header *__register_sysctl_table( spin_lock(&sysctl_lock); header->set = set = lookup_header_set(root, namespaces); - dir = &sysctl_root_dir; + dir = &set->dir; dir->header.nreg++; spin_unlock(&sysctl_lock); @@ -1012,22 +1163,28 @@ struct ctl_table_header *__register_sysctl_table( if (namelen == 0) continue; - dir = get_subdir(set, dir, name, namelen); + dir = get_subdir(dir, name, namelen); if (IS_ERR(dir)) goto fail; } + spin_lock(&sysctl_lock); - if (sysctl_check_dups(namespaces, dir, path, table)) + if (sysctl_check_dups(dir, path, table)) + goto fail_put_dir_locked; + + if (insert_header(dir, header)) goto fail_put_dir_locked; - insert_header(dir, header); + drop_sysctl_table(&dir->header); spin_unlock(&sysctl_lock); return header; + fail_put_dir_locked: drop_sysctl_table(&dir->header); spin_unlock(&sysctl_lock); fail: + kfree(links); kfree(header); dump_stack(); return NULL; @@ -1249,6 +1406,40 @@ struct ctl_table_header *register_sysctl_table(struct ctl_table *table) } EXPORT_SYMBOL(register_sysctl_table); +static void put_links(struct ctl_table_header *header) +{ + struct ctl_table_set *root_set = &sysctl_table_root.default_set; + struct ctl_table_root *root = header->root; + struct ctl_dir *parent = header->parent; + struct ctl_dir *core_parent; + struct ctl_table *entry; + + if (header->set == root_set) + return; + + core_parent = xlate_dir(root_set, parent); + if (IS_ERR(core_parent)) + return; + + for (entry = header->ctl_table; entry->procname; entry++) { + struct ctl_table_header *link_head; + struct ctl_table *link; + const char *name = entry->procname; + + link = find_entry(&link_head, core_parent, name, strlen(name)); + if (link && + ((S_ISDIR(link->mode) && S_ISDIR(entry->mode)) || + (S_ISLNK(link->mode) && (link->data == root)))) { + drop_sysctl_table(link_head); + } + else { + printk(KERN_ERR "sysctl link missing during unregister: "); + sysctl_print_dir(parent); + printk(KERN_CONT "/%s\n", name); + } + } +} + static void drop_sysctl_table(struct ctl_table_header *header) { struct ctl_dir *parent = header->parent; @@ -1256,6 +1447,7 @@ static void drop_sysctl_table(struct ctl_table_header *header) if (--header->nreg) return; + put_links(header); start_unregistering(header); if (!--header->count) kfree_rcu(header, rcu); @@ -1301,13 +1493,14 @@ void unregister_sysctl_table(struct ctl_table_header * header) } EXPORT_SYMBOL(unregister_sysctl_table); -void setup_sysctl_set(struct ctl_table_set *p, +void setup_sysctl_set(struct ctl_table_set *set, struct ctl_table_root *root, int (*is_seen)(struct ctl_table_set *)) { - INIT_LIST_HEAD(&p->list); - p->root = root; - p->is_seen = is_seen; + memset(set, sizeof(*set), 0); + INIT_LIST_HEAD(&set->list); + set->is_seen = is_seen; + init_header(&set->dir.header, root, set, root_table); } void retire_sysctl_set(struct ctl_table_set *set) diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index 3084b624868c..2a1446a96094 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -1051,12 +1051,11 @@ struct ctl_dir { struct ctl_table_set { struct list_head list; - struct ctl_table_root *root; int (*is_seen)(struct ctl_table_set *); + struct ctl_dir dir; }; struct ctl_table_root { - struct list_head root_list; struct ctl_table_set default_set; struct ctl_table_set *(*lookup)(struct ctl_table_root *root, struct nsproxy *namespaces); -- cgit v1.2.3 From 60a47a2e823cbe6b609346bffff61a00c0c76470 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Sun, 8 Jan 2012 00:02:37 -0800 Subject: sysctl: Modify __register_sysctl_paths to take a set instead of a root and an nsproxy An nsproxy argument here has always been awkard and now the nsproxy argument is completely unnecessary so remove it, replacing it with the set we want the registered tables to show up in. Signed-off-by: Eric W. Biederman --- fs/proc/proc_sysctl.c | 30 ++++++++++++------------------ include/linux/sysctl.h | 4 ++-- net/sysctl_net.c | 10 +++------- 3 files changed, 17 insertions(+), 27 deletions(-) (limited to 'include/linux') diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c index ec54a57c4690..e0d3e7e59cbd 100644 --- a/fs/proc/proc_sysctl.c +++ b/fs/proc/proc_sysctl.c @@ -1084,8 +1084,7 @@ out: /** * __register_sysctl_table - register a leaf sysctl table - * @root: List of sysctl headers to register on - * @namespaces: Data to compute which lists of sysctl entries are visible + * @set: Sysctl tree to register on * @path: The path to the directory the sysctl table is in. * @table: the top-level table structure * @@ -1126,26 +1125,24 @@ out: * to the table header on success. */ struct ctl_table_header *__register_sysctl_table( - struct ctl_table_root *root, - struct nsproxy *namespaces, + struct ctl_table_set *set, const char *path, struct ctl_table *table) { + struct ctl_table_root *root = set->dir.header.root; struct ctl_table_header *links = NULL; struct ctl_table_header *header; const char *name, *nextname; - struct ctl_table_set *set; struct ctl_dir *dir; header = kzalloc(sizeof(struct ctl_table_header), GFP_KERNEL); if (!header) return NULL; - init_header(header, root, NULL, table); + init_header(header, root, set, table); if (sysctl_check_table(path, table)) goto fail; spin_lock(&sysctl_lock); - header->set = set = lookup_header_set(root, namespaces); dir = &set->dir; dir->header.nreg++; spin_unlock(&sysctl_lock); @@ -1223,8 +1220,7 @@ static int count_subheaders(struct ctl_table *table) } static int register_leaf_sysctl_tables(const char *path, char *pos, - struct ctl_table_header ***subheader, - struct ctl_table_root *root, struct nsproxy *namespaces, + struct ctl_table_header ***subheader, struct ctl_table_set *set, struct ctl_table *table) { struct ctl_table *ctl_table_arg = NULL; @@ -1261,7 +1257,7 @@ static int register_leaf_sysctl_tables(const char *path, char *pos, /* Register everything except a directory full of subdirectories */ if (nr_files || !nr_dirs) { struct ctl_table_header *header; - header = __register_sysctl_table(root, namespaces, path, files); + header = __register_sysctl_table(set, path, files); if (!header) { kfree(ctl_table_arg); goto out; @@ -1286,7 +1282,7 @@ static int register_leaf_sysctl_tables(const char *path, char *pos, goto out; err = register_leaf_sysctl_tables(path, child_pos, subheader, - root, namespaces, entry->child); + set, entry->child); pos[0] = '\0'; if (err) goto out; @@ -1299,8 +1295,7 @@ out: /** * __register_sysctl_paths - register a sysctl table hierarchy - * @root: List of sysctl headers to register on - * @namespaces: Data to compute which lists of sysctl entries are visible + * @set: Sysctl tree to register on * @path: The path to the directory the sysctl table is in. * @table: the top-level table structure * @@ -1310,8 +1305,7 @@ out: * See __register_sysctl_table for more details. */ struct ctl_table_header *__register_sysctl_paths( - struct ctl_table_root *root, - struct nsproxy *namespaces, + struct ctl_table_set *set, const struct ctl_path *path, struct ctl_table *table) { struct ctl_table *ctl_table_arg = table; @@ -1337,7 +1331,7 @@ struct ctl_table_header *__register_sysctl_paths( table = table->child; } if (nr_subheaders == 1) { - header = __register_sysctl_table(root, namespaces, new_path, table); + header = __register_sysctl_table(set, new_path, table); if (header) header->ctl_table_arg = ctl_table_arg; } else { @@ -1351,7 +1345,7 @@ struct ctl_table_header *__register_sysctl_paths( header->ctl_table_arg = ctl_table_arg; if (register_leaf_sysctl_tables(new_path, pos, &subheader, - root, namespaces, table)) + set, table)) goto err_register_leaves; } @@ -1384,7 +1378,7 @@ err_register_leaves: struct ctl_table_header *register_sysctl_paths(const struct ctl_path *path, struct ctl_table *table) { - return __register_sysctl_paths(&sysctl_table_root, current->nsproxy, + return __register_sysctl_paths(&sysctl_table_root.default_set, path, table); } EXPORT_SYMBOL(register_sysctl_paths); diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index 2a1446a96094..cec59415b3ce 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -1079,10 +1079,10 @@ extern void retire_sysctl_set(struct ctl_table_set *set); void register_sysctl_root(struct ctl_table_root *root); struct ctl_table_header *__register_sysctl_table( - struct ctl_table_root *root, struct nsproxy *namespaces, + struct ctl_table_set *set, const char *path, struct ctl_table *table); struct ctl_table_header *__register_sysctl_paths( - struct ctl_table_root *root, struct nsproxy *namespaces, + struct ctl_table_set *set, const struct ctl_path *path, struct ctl_table *table); struct ctl_table_header *register_sysctl_table(struct ctl_table * table); struct ctl_table_header *register_sysctl_paths(const struct ctl_path *path, diff --git a/net/sysctl_net.c b/net/sysctl_net.c index e998c6448046..c3e65aebecc0 100644 --- a/net/sysctl_net.c +++ b/net/sysctl_net.c @@ -105,19 +105,15 @@ subsys_initcall(net_sysctl_init); struct ctl_table_header *register_net_sysctl_table(struct net *net, const struct ctl_path *path, struct ctl_table *table) { - struct nsproxy namespaces; - namespaces = *current->nsproxy; - namespaces.net_ns = net; - return __register_sysctl_paths(&net_sysctl_root, - &namespaces, path, table); + return __register_sysctl_paths(&net->sysctls, path, table); } EXPORT_SYMBOL_GPL(register_net_sysctl_table); struct ctl_table_header *register_net_sysctl_rotable(const struct ctl_path *path, struct ctl_table *table) { - return __register_sysctl_paths(&net_sysctl_ro_root, - &init_nsproxy, path, table); + return __register_sysctl_paths(&net_sysctl_ro_root.default_set, + path, table); } EXPORT_SYMBOL_GPL(register_net_sysctl_rotable); -- cgit v1.2.3 From 9e3d47df35abd6430fed04fb40a76c7358b1e815 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Sat, 7 Jan 2012 23:45:12 -0800 Subject: sysctl: Make the header lists per directory. Slightly enhance efficiency and clarity of the code by making the header list per directory instead of per set. Benchmark before: make-dummies 0 999 -> 0.63s rmmod dummy -> 0.12s make-dummies 0 9999 -> 2m35s rmmod dummy -> 18s Benchmark after: make-dummies 0 999 -> 0.32s rmmod dummy -> 0.12s make-dummies 0 9999 -> 1m17s rmmod dummy -> 17s Signed-off-by: Eric W. Biederman --- fs/proc/proc_sysctl.c | 28 +++++++++++----------------- include/linux/sysctl.h | 2 +- 2 files changed, 12 insertions(+), 18 deletions(-) (limited to 'include/linux') diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c index 160d5781638e..e971ccccac4a 100644 --- a/fs/proc/proc_sysctl.c +++ b/fs/proc/proc_sysctl.c @@ -33,12 +33,12 @@ static struct ctl_table root_table[] = { { } }; static struct ctl_table_root sysctl_table_root = { - .default_set.list = LIST_HEAD_INIT(sysctl_table_root.default_set.dir.header.ctl_entry), + .default_set.dir.list = LIST_HEAD_INIT(sysctl_table_root.default_set.dir.list), .default_set.dir.header = { {{.count = 1, .nreg = 1, .ctl_table = root_table, - .ctl_entry = LIST_HEAD_INIT(sysctl_table_root.default_set.list),}}, + .ctl_entry = LIST_HEAD_INIT(sysctl_table_root.default_set.dir.header.ctl_entry),}}, .ctl_table_arg = root_table, .root = &sysctl_table_root, .set = &sysctl_table_root.default_set, @@ -79,15 +79,12 @@ static int namecmp(const char *name1, int len1, const char *name2, int len2) static struct ctl_table *find_entry(struct ctl_table_header **phead, struct ctl_dir *dir, const char *name, int namelen) { - struct ctl_table_set *set = dir->header.set; struct ctl_table_header *head; struct ctl_table *entry; - list_for_each_entry(head, &set->list, ctl_entry) { + list_for_each_entry(head, &dir->list, ctl_entry) { if (head->unregistering) continue; - if (head->parent != dir) - continue; for (entry = head->ctl_table; entry->procname; entry++) { const char *procname = entry->procname; if (namecmp(procname, strlen(procname), name, namelen) == 0) { @@ -133,7 +130,7 @@ static int insert_header(struct ctl_dir *dir, struct ctl_table_header *header) err = insert_links(header); if (err) goto fail_links; - list_add_tail(&header->ctl_entry, &header->set->list); + list_add_tail(&header->ctl_entry, &header->parent->list); return 0; fail_links: header->parent = NULL; @@ -247,14 +244,12 @@ static struct ctl_table *lookup_entry(struct ctl_table_header **phead, static struct ctl_table_header *next_usable_entry(struct ctl_dir *dir, struct list_head *tmp) { - struct ctl_table_set *set = dir->header.set; struct ctl_table_header *head; - for (tmp = tmp->next; tmp != &set->list; tmp = tmp->next) { + for (tmp = tmp->next; tmp != &dir->list; tmp = tmp->next) { head = list_entry(tmp, struct ctl_table_header, ctl_entry); - if (head->parent != dir || - !head->ctl_table->procname || + if (!head->ctl_table->procname || !use_table(head)) continue; @@ -270,7 +265,7 @@ static void first_entry(struct ctl_dir *dir, struct ctl_table *entry = NULL; spin_lock(&sysctl_lock); - head = next_usable_entry(dir, &dir->header.set->list); + head = next_usable_entry(dir, &dir->list); spin_unlock(&sysctl_lock); if (head) entry = head->ctl_table; @@ -793,6 +788,7 @@ static struct ctl_dir *new_dir(struct ctl_table_set *set, new_name = (char *)(table + 2); memcpy(new_name, name, namelen); new_name[namelen] = '\0'; + INIT_LIST_HEAD(&new->list); table[0].procname = new_name; table[0].mode = S_IFDIR|S_IRUGO|S_IXUGO; init_header(&new->header, set->dir.header.root, set, table); @@ -917,12 +913,10 @@ static int sysctl_check_table_dups(struct ctl_dir *dir, struct ctl_table *old, static int sysctl_check_dups(struct ctl_dir *dir, struct ctl_table *table) { - struct ctl_table_set *set; struct ctl_table_header *head; int error = 0; - set = dir->header.set; - list_for_each_entry(head, &set->list, ctl_entry) { + list_for_each_entry(head, &dir->list, ctl_entry) { if (head->unregistering) continue; if (head->parent != dir) @@ -1494,14 +1488,14 @@ void setup_sysctl_set(struct ctl_table_set *set, int (*is_seen)(struct ctl_table_set *)) { memset(set, sizeof(*set), 0); - INIT_LIST_HEAD(&set->list); set->is_seen = is_seen; + INIT_LIST_HEAD(&set->dir.list); init_header(&set->dir.header, root, set, root_table); } void retire_sysctl_set(struct ctl_table_set *set) { - WARN_ON(!list_empty(&set->list)); + WARN_ON(!list_empty(&set->dir.list)); } int __init proc_sys_init(void) diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index cec59415b3ce..36dec756ef9d 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -1047,10 +1047,10 @@ struct ctl_table_header struct ctl_dir { /* Header must be at the start of ctl_dir */ struct ctl_table_header header; + struct list_head list; }; struct ctl_table_set { - struct list_head list; int (*is_seen)(struct ctl_table_set *); struct ctl_dir dir; }; -- cgit v1.2.3 From ac13ac6f4c6c0504d2c927862216f4e422a2c0b5 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Mon, 9 Jan 2012 17:24:30 -0800 Subject: sysctl: Index sysctl directories with rbtrees. One of the most important jobs of sysctl is to export network stack tunables. Several of those tunables are per network device. In several instances people are running with 1000+ network devices in there network stacks, which makes the simple per directory linked list in sysctl a scaling bottleneck. Replace O(N^2) sysctl insertion and lookup times with O(NlogN) by using an rbtree to index the sysctl directories. Benchmark before: make-dummies 0 999 -> 0.32s rmmod dummy -> 0.12s make-dummies 0 9999 -> 1m17s rmmod dummy -> 17s Benchmark after: make-dummies 0 999 -> 0.074s rmmod dummy -> 0.070s make-dummies 0 9999 -> 3.4s rmmod dummy -> 0.44s Benchmark after (without dev_snmp6): make-dummies 0 9999 -> 0.75s rmmod dummy -> 0.44s make-dummies 0 99999 -> 11s rmmod dummy -> 4.3s At 10,000 dummy devices the bottleneck becomes the time to add and remove the files under /proc/sys/net/dev_snmp6. I have commented out the code that adds and removes files under /proc/sys/net/dev_snmp6 and taken measurments of creating and destroying 100,000 dummies to verify the sysctl continues to scale. Signed-off-by: Eric W. Biederman --- fs/proc/proc_sysctl.c | 224 +++++++++++++++++++++++++++++-------------------- include/linux/sysctl.h | 10 ++- 2 files changed, 142 insertions(+), 92 deletions(-) (limited to 'include/linux') diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c index e971ccccac4a..05c393a5c530 100644 --- a/fs/proc/proc_sysctl.c +++ b/fs/proc/proc_sysctl.c @@ -33,12 +33,10 @@ static struct ctl_table root_table[] = { { } }; static struct ctl_table_root sysctl_table_root = { - .default_set.dir.list = LIST_HEAD_INIT(sysctl_table_root.default_set.dir.list), .default_set.dir.header = { {{.count = 1, .nreg = 1, - .ctl_table = root_table, - .ctl_entry = LIST_HEAD_INIT(sysctl_table_root.default_set.dir.header.ctl_entry),}}, + .ctl_table = root_table }}, .ctl_table_arg = root_table, .root = &sysctl_table_root, .set = &sysctl_table_root.default_set, @@ -52,7 +50,6 @@ static int sysctl_follow_link(struct ctl_table_header **phead, struct ctl_table **pentry, struct nsproxy *namespaces); static int insert_links(struct ctl_table_header *head); static void put_links(struct ctl_table_header *header); -static int sysctl_check_dups(struct ctl_dir *dir, struct ctl_table *table); static void sysctl_print_dir(struct ctl_dir *dir) { @@ -81,28 +78,83 @@ static struct ctl_table *find_entry(struct ctl_table_header **phead, { struct ctl_table_header *head; struct ctl_table *entry; + struct rb_node *node = dir->root.rb_node; - list_for_each_entry(head, &dir->list, ctl_entry) { - if (head->unregistering) - continue; - for (entry = head->ctl_table; entry->procname; entry++) { - const char *procname = entry->procname; - if (namecmp(procname, strlen(procname), name, namelen) == 0) { - *phead = head; - return entry; - } + while (node) + { + struct ctl_node *ctl_node; + const char *procname; + int cmp; + + ctl_node = rb_entry(node, struct ctl_node, node); + head = ctl_node->header; + entry = &head->ctl_table[ctl_node - head->node]; + procname = entry->procname; + + cmp = namecmp(name, namelen, procname, strlen(procname)); + if (cmp < 0) + node = node->rb_left; + else if (cmp > 0) + node = node->rb_right; + else { + *phead = head; + return entry; } } return NULL; } +static int insert_entry(struct ctl_table_header *head, struct ctl_table *entry) +{ + struct rb_node *node = &head->node[entry - head->ctl_table].node; + struct rb_node **p = &head->parent->root.rb_node; + struct rb_node *parent = NULL; + const char *name = entry->procname; + int namelen = strlen(name); + + while (*p) { + struct ctl_table_header *parent_head; + struct ctl_table *parent_entry; + struct ctl_node *parent_node; + const char *parent_name; + int cmp; + + parent = *p; + parent_node = rb_entry(parent, struct ctl_node, node); + parent_head = parent_node->header; + parent_entry = &parent_head->ctl_table[parent_node - parent_head->node]; + parent_name = parent_entry->procname; + + cmp = namecmp(name, namelen, parent_name, strlen(parent_name)); + if (cmp < 0) + p = &(*p)->rb_left; + else if (cmp > 0) + p = &(*p)->rb_right; + else { + printk(KERN_ERR "sysctl duplicate entry: "); + sysctl_print_dir(head->parent); + printk(KERN_CONT "/%s\n", entry->procname); + return -EEXIST; + } + } + + rb_link_node(node, parent, p); + return 0; +} + +static void erase_entry(struct ctl_table_header *head, struct ctl_table *entry) +{ + struct rb_node *node = &head->node[entry - head->ctl_table].node; + + rb_erase(node, &head->parent->root); +} + static void init_header(struct ctl_table_header *head, struct ctl_table_root *root, struct ctl_table_set *set, - struct ctl_table *table) + struct ctl_node *node, struct ctl_table *table) { head->ctl_table = table; head->ctl_table_arg = table; - INIT_LIST_HEAD(&head->ctl_entry); head->used = 0; head->count = 1; head->nreg = 1; @@ -110,28 +162,42 @@ static void init_header(struct ctl_table_header *head, head->root = root; head->set = set; head->parent = NULL; + head->node = node; + if (node) { + struct ctl_table *entry; + for (entry = table; entry->procname; entry++, node++) { + rb_init_node(&node->node); + node->header = head; + } + } } static void erase_header(struct ctl_table_header *head) { - list_del_init(&head->ctl_entry); + struct ctl_table *entry; + for (entry = head->ctl_table; entry->procname; entry++) + erase_entry(head, entry); } static int insert_header(struct ctl_dir *dir, struct ctl_table_header *header) { + struct ctl_table *entry; int err; - err = sysctl_check_dups(dir, header->ctl_table); - if (err) - return err; - dir->header.nreg++; header->parent = dir; err = insert_links(header); if (err) goto fail_links; - list_add_tail(&header->ctl_entry, &header->parent->list); + for (entry = header->ctl_table; entry->procname; entry++) { + err = insert_entry(header, entry); + if (err) + goto fail; + } return 0; +fail: + erase_header(header); + put_links(header); fail_links: header->parent = NULL; drop_sysctl_table(&dir->header); @@ -241,19 +307,14 @@ static struct ctl_table *lookup_entry(struct ctl_table_header **phead, return entry; } -static struct ctl_table_header *next_usable_entry(struct ctl_dir *dir, - struct list_head *tmp) +static struct ctl_node *first_usable_entry(struct rb_node *node) { - struct ctl_table_header *head; - - for (tmp = tmp->next; tmp != &dir->list; tmp = tmp->next) { - head = list_entry(tmp, struct ctl_table_header, ctl_entry); + struct ctl_node *ctl_node; - if (!head->ctl_table->procname || - !use_table(head)) - continue; - - return head; + for (;node; node = rb_next(node)) { + ctl_node = rb_entry(node, struct ctl_node, node); + if (use_table(ctl_node->header)) + return ctl_node; } return NULL; } @@ -261,14 +322,17 @@ static struct ctl_table_header *next_usable_entry(struct ctl_dir *dir, static void first_entry(struct ctl_dir *dir, struct ctl_table_header **phead, struct ctl_table **pentry) { - struct ctl_table_header *head; + struct ctl_table_header *head = NULL; struct ctl_table *entry = NULL; + struct ctl_node *ctl_node; spin_lock(&sysctl_lock); - head = next_usable_entry(dir, &dir->list); + ctl_node = first_usable_entry(rb_first(&dir->root)); spin_unlock(&sysctl_lock); - if (head) - entry = head->ctl_table; + if (ctl_node) { + head = ctl_node->header; + entry = &head->ctl_table[ctl_node - head->node]; + } *phead = head; *pentry = entry; } @@ -277,15 +341,17 @@ static void next_entry(struct ctl_table_header **phead, struct ctl_table **pentr { struct ctl_table_header *head = *phead; struct ctl_table *entry = *pentry; + struct ctl_node *ctl_node = &head->node[entry - head->ctl_table]; - entry++; - if (!entry->procname) { - spin_lock(&sysctl_lock); - unuse_table(head); - head = next_usable_entry(head->parent, &head->ctl_entry); - spin_unlock(&sysctl_lock); - if (head) - entry = head->ctl_table; + spin_lock(&sysctl_lock); + unuse_table(head); + + ctl_node = first_usable_entry(rb_next(&ctl_node->node)); + spin_unlock(&sysctl_lock); + head = NULL; + if (ctl_node) { + head = ctl_node->header; + entry = &head->ctl_table[ctl_node - head->node]; } *phead = head; *pentry = entry; @@ -777,21 +843,23 @@ static struct ctl_dir *new_dir(struct ctl_table_set *set, { struct ctl_table *table; struct ctl_dir *new; + struct ctl_node *node; char *new_name; - new = kzalloc(sizeof(*new) + sizeof(struct ctl_table)*2 + - namelen + 1, GFP_KERNEL); + new = kzalloc(sizeof(*new) + sizeof(struct ctl_node) + + sizeof(struct ctl_table)*2 + namelen + 1, + GFP_KERNEL); if (!new) return NULL; - table = (struct ctl_table *)(new + 1); + node = (struct ctl_node *)(new + 1); + table = (struct ctl_table *)(node + 1); new_name = (char *)(table + 2); memcpy(new_name, name, namelen); new_name[namelen] = '\0'; - INIT_LIST_HEAD(&new->list); table[0].procname = new_name; table[0].mode = S_IFDIR|S_IRUGO|S_IXUGO; - init_header(&new->header, set->dir.header.root, set, table); + init_header(&new->header, set->dir.header.root, set, node, table); return new; } @@ -892,40 +960,6 @@ static int sysctl_follow_link(struct ctl_table_header **phead, return ret; } -static int sysctl_check_table_dups(struct ctl_dir *dir, struct ctl_table *old, - struct ctl_table *table) -{ - struct ctl_table *entry, *test; - int error = 0; - - for (entry = old; entry->procname; entry++) { - for (test = table; test->procname; test++) { - if (strcmp(entry->procname, test->procname) == 0) { - printk(KERN_ERR "sysctl duplicate entry: "); - sysctl_print_dir(dir); - printk(KERN_CONT "/%s\n", test->procname); - error = -EEXIST; - } - } - } - return error; -} - -static int sysctl_check_dups(struct ctl_dir *dir, struct ctl_table *table) -{ - struct ctl_table_header *head; - int error = 0; - - list_for_each_entry(head, &dir->list, ctl_entry) { - if (head->unregistering) - continue; - if (head->parent != dir) - continue; - error = sysctl_check_table_dups(dir, head->ctl_table, table); - } - return error; -} - static int sysctl_err(const char *path, struct ctl_table *table, char *fmt, ...) { struct va_format vaf; @@ -977,6 +1011,7 @@ static struct ctl_table_header *new_links(struct ctl_dir *dir, struct ctl_table { struct ctl_table *link_table, *entry, *link; struct ctl_table_header *links; + struct ctl_node *node; char *link_name; int nr_entries, name_bytes; @@ -988,6 +1023,7 @@ static struct ctl_table_header *new_links(struct ctl_dir *dir, struct ctl_table } links = kzalloc(sizeof(struct ctl_table_header) + + sizeof(struct ctl_node)*nr_entries + sizeof(struct ctl_table)*(nr_entries + 1) + name_bytes, GFP_KERNEL); @@ -995,7 +1031,8 @@ static struct ctl_table_header *new_links(struct ctl_dir *dir, struct ctl_table if (!links) return NULL; - link_table = (struct ctl_table *)(links + 1); + node = (struct ctl_node *)(links + 1); + link_table = (struct ctl_table *)(node + nr_entries); link_name = (char *)&link_table[nr_entries + 1]; for (link = link_table, entry = table; entry->procname; link++, entry++) { @@ -1006,7 +1043,7 @@ static struct ctl_table_header *new_links(struct ctl_dir *dir, struct ctl_table link->data = link_root; link_name += len; } - init_header(links, dir->header.root, dir->header.set, link_table); + init_header(links, dir->header.root, dir->header.set, node, link_table); links->nreg = nr_entries; return links; @@ -1132,12 +1169,20 @@ struct ctl_table_header *__register_sysctl_table( struct ctl_table_header *header; const char *name, *nextname; struct ctl_dir *dir; + struct ctl_table *entry; + struct ctl_node *node; + int nr_entries = 0; + + for (entry = table; entry->procname; entry++) + nr_entries++; - header = kzalloc(sizeof(struct ctl_table_header), GFP_KERNEL); + header = kzalloc(sizeof(struct ctl_table_header) + + sizeof(struct ctl_node)*nr_entries, GFP_KERNEL); if (!header) return NULL; - init_header(header, root, set, table); + node = (struct ctl_node *)(header + 1); + init_header(header, root, set, node, table); if (sysctl_check_table(path, table)) goto fail; @@ -1489,13 +1534,12 @@ void setup_sysctl_set(struct ctl_table_set *set, { memset(set, sizeof(*set), 0); set->is_seen = is_seen; - INIT_LIST_HEAD(&set->dir.list); - init_header(&set->dir.header, root, set, root_table); + init_header(&set->dir.header, root, set, NULL, root_table); } void retire_sysctl_set(struct ctl_table_set *set) { - WARN_ON(!list_empty(&set->dir.list)); + WARN_ON(!RB_EMPTY_ROOT(&set->dir.root)); } int __init proc_sys_init(void) diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index 36dec756ef9d..35c50ed36fc9 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -932,6 +932,7 @@ enum #include #include #include +#include /* For the /proc/sys support */ struct ctl_table; @@ -1023,6 +1024,11 @@ struct ctl_table void *extra2; }; +struct ctl_node { + struct rb_node node; + struct ctl_table_header *header; +}; + /* struct ctl_table_header is used to maintain dynamic lists of struct ctl_table trees. */ struct ctl_table_header @@ -1030,7 +1036,6 @@ struct ctl_table_header union { struct { struct ctl_table *ctl_table; - struct list_head ctl_entry; int used; int count; int nreg; @@ -1042,12 +1047,13 @@ struct ctl_table_header struct ctl_table_root *root; struct ctl_table_set *set; struct ctl_dir *parent; + struct ctl_node *node; }; struct ctl_dir { /* Header must be at the start of ctl_dir */ struct ctl_table_header header; - struct list_head list; + struct rb_root root; }; struct ctl_table_set { -- cgit v1.2.3 From fea478d4101a4285aa25c5bafaaf4cec35026fe0 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Fri, 20 Jan 2012 21:47:03 -0800 Subject: sysctl: Add register_sysctl for normal sysctl users The plan is to convert all callers of register_sysctl_table and register_sysctl_paths to register_sysctl. The interface to register_sysctl is enough nicer this should make the callers a bit more readable. Additionally after the conversion the 230 lines of backwards compatibility can be removed. Signed-off-by: Eric W. Biederman --- fs/proc/proc_sysctl.c | 17 +++++++++++++++++ include/linux/sysctl.h | 1 + 2 files changed, 18 insertions(+) (limited to 'include/linux') diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c index 05c393a5c530..8dc7f0e46e7e 100644 --- a/fs/proc/proc_sysctl.c +++ b/fs/proc/proc_sysctl.c @@ -1228,6 +1228,23 @@ fail: return NULL; } +/** + * register_sysctl - register a sysctl table + * @path: The path to the directory the sysctl table is in. + * @table: the table structure + * + * Register a sysctl table. @table should be a filled in ctl_table + * array. A completely 0 filled entry terminates the table. + * + * See __register_sysctl_table for more details. + */ +struct ctl_table_header *register_sysctl(const char *path, struct ctl_table *table) +{ + return __register_sysctl_table(&sysctl_table_root.default_set, + path, table); +} +EXPORT_SYMBOL(register_sysctl); + static char *append_path(const char *path, char *pos, const char *name) { int namelen; diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index 35c50ed36fc9..c34b4c82b0dc 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -1090,6 +1090,7 @@ struct ctl_table_header *__register_sysctl_table( struct ctl_table_header *__register_sysctl_paths( struct ctl_table_set *set, const struct ctl_path *path, struct ctl_table *table); +struct ctl_table_header *register_sysctl(const char *path, struct ctl_table *table); struct ctl_table_header *register_sysctl_table(struct ctl_table * table); struct ctl_table_header *register_sysctl_paths(const struct ctl_path *path, struct ctl_table *table); -- cgit v1.2.3 From d5dc9271b25822f36d299f8cab985379743424b9 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 18 Dec 2011 11:07:47 +0000 Subject: ARM: amba: add amba_device allocation/add/put functions Add functions to allocate and initialize AMBA device structures, and add them to the Linux device manager. This allows us to kill this type of operation from individual platforms, moving it to core code. Signed-off-by: Russell King --- drivers/amba/bus.c | 100 +++++++++++++++++++++++++++++++++++------------ include/linux/amba/bus.h | 3 ++ 2 files changed, 78 insertions(+), 25 deletions(-) (limited to 'include/linux') diff --git a/drivers/amba/bus.c b/drivers/amba/bus.c index 54eaf96ab217..82b65e1e12bf 100644 --- a/drivers/amba/bus.c +++ b/drivers/amba/bus.c @@ -497,38 +497,20 @@ static void amba_device_release(struct device *dev) } /** - * amba_device_register - register an AMBA device - * @dev: AMBA device to register - * @parent: parent memory resource + * amba_device_add - add a previously allocated AMBA device structure + * @dev: AMBA device allocated by amba_device_alloc + * @parent: resource parent for this devices resources * - * Setup the AMBA device, reading the cell ID if present. - * Claim the resource, and register the AMBA device with - * the Linux device manager. + * Claim the resource, and read the device cell ID if not already + * initialized. Register the AMBA device with the Linux device + * manager. */ -int amba_device_register(struct amba_device *dev, struct resource *parent) +int amba_device_add(struct amba_device *dev, struct resource *parent) { u32 size; void __iomem *tmp; int i, ret; - device_initialize(&dev->dev); - - /* - * Copy from device_add - */ - if (dev->dev.init_name) { - dev_set_name(&dev->dev, "%s", dev->dev.init_name); - dev->dev.init_name = NULL; - } - - dev->dev.release = amba_device_release; - dev->dev.bus = &amba_bustype; - dev->dev.dma_mask = &dev->dma_mask; - dev->res.name = dev_name(&dev->dev); - - if (!dev->dev.coherent_dma_mask && dev->dma_mask) - dev_warn(&dev->dev, "coherent dma mask is unset\n"); - ret = request_resource(parent, &dev->res); if (ret) goto err_out; @@ -596,6 +578,74 @@ int amba_device_register(struct amba_device *dev, struct resource *parent) err_out: return ret; } +EXPORT_SYMBOL_GPL(amba_device_add); + +static void amba_device_initialize(struct amba_device *dev, const char *name) +{ + device_initialize(&dev->dev); + if (name) + dev_set_name(&dev->dev, "%s", name); + dev->dev.release = amba_device_release; + dev->dev.bus = &amba_bustype; + dev->dev.dma_mask = &dev->dma_mask; + dev->res.name = dev_name(&dev->dev); +} + +/** + * amba_device_alloc - allocate an AMBA device + * @name: sysfs name of the AMBA device + * @base: base of AMBA device + * @size: size of AMBA device + * + * Allocate and initialize an AMBA device structure. Returns %NULL + * on failure. + */ +struct amba_device *amba_device_alloc(const char *name, resource_size_t base, + size_t size) +{ + struct amba_device *dev; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (dev) { + amba_device_initialize(dev, name); + dev->res.start = base; + dev->res.end = base + size - 1; + dev->res.flags = IORESOURCE_MEM; + } + + return dev; +} +EXPORT_SYMBOL_GPL(amba_device_alloc); + +/** + * amba_device_register - register an AMBA device + * @dev: AMBA device to register + * @parent: parent memory resource + * + * Setup the AMBA device, reading the cell ID if present. + * Claim the resource, and register the AMBA device with + * the Linux device manager. + */ +int amba_device_register(struct amba_device *dev, struct resource *parent) +{ + amba_device_initialize(dev, dev->dev.init_name); + dev->dev.init_name = NULL; + + if (!dev->dev.coherent_dma_mask && dev->dma_mask) + dev_warn(&dev->dev, "coherent dma mask is unset\n"); + + return amba_device_add(dev, parent); +} + +/** + * amba_device_put - put an AMBA device + * @dev: AMBA device to put + */ +void amba_device_put(struct amba_device *dev) +{ + put_device(&dev->dev); +} +EXPORT_SYMBOL_GPL(amba_device_put); /** * amba_device_unregister - unregister an AMBA device diff --git a/include/linux/amba/bus.h b/include/linux/amba/bus.h index 724c69c40bb8..e1929620e5a8 100644 --- a/include/linux/amba/bus.h +++ b/include/linux/amba/bus.h @@ -60,6 +60,9 @@ extern struct bus_type amba_bustype; int amba_driver_register(struct amba_driver *); void amba_driver_unregister(struct amba_driver *); +struct amba_device *amba_device_alloc(const char *, resource_size_t, size_t); +void amba_device_put(struct amba_device *); +int amba_device_add(struct amba_device *, struct resource *); int amba_device_register(struct amba_device *, struct resource *); void amba_device_unregister(struct amba_device *); struct amba_device *amba_find_device(const char *, struct device *, unsigned int, unsigned int); -- cgit v1.2.3 From cc6e75af8df7aa40019ff58357170c8dd960281f Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 18 Dec 2011 12:06:25 +0000 Subject: ARM: amba: provide common initializers for static amba devices Acked-by: H Hartley Sweeten Signed-off-by: Russell King --- include/linux/amba/bus.h | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) (limited to 'include/linux') diff --git a/include/linux/amba/bus.h b/include/linux/amba/bus.h index e1929620e5a8..a9fab831caf8 100644 --- a/include/linux/amba/bus.h +++ b/include/linux/amba/bus.h @@ -92,4 +92,37 @@ void amba_release_regions(struct amba_device *); #define amba_manf(d) AMBA_MANF_BITS((d)->periphid) #define amba_part(d) AMBA_PART_BITS((d)->periphid) +#define __AMBA_DEV(busid, data, mask) \ + { \ + .coherent_dma_mask = mask, \ + .init_name = busid, \ + .platform_data = data, \ + } + +/* + * APB devices do not themselves have the ability to address memory, + * so DMA masks should be zero (much like USB peripheral devices.) + * The DMA controller DMA masks should be used instead (much like + * USB host controllers in conventional PCs.) + */ +#define AMBA_APB_DEVICE(name, busid, id, base, irqs, data) \ +struct amba_device name##_device = { \ + .dev = __AMBA_DEV(busid, data, 0), \ + .res = DEFINE_RES_MEM(base, SZ_4K), \ + .irq = irqs, \ + .periphid = id, \ +} + +/* + * AHB devices are DMA capable, so set their DMA masks + */ +#define AMBA_AHB_DEVICE(name, busid, id, base, irqs, data) \ +struct amba_device name##_device = { \ + .dev = __AMBA_DEV(busid, data, ~0ULL), \ + .res = DEFINE_RES_MEM(base, SZ_4K), \ + .dma_mask = ~0ULL, \ + .irq = irqs, \ + .periphid = id, \ +} + #endif -- cgit v1.2.3 From 92a9c19a89af2ca219fbb040a0059f414a4b7223 Mon Sep 17 00:00:00 2001 From: Kay Sievers Date: Sat, 28 Jan 2012 19:57:46 +0000 Subject: udlfb: remove sysfs framebuffer device with USB .disconnect() The USB graphics card driver delays the unregistering of the framebuffer device to a workqueue, which breaks the userspace visible remove uevent sequence. Recent userspace tools started to support USB graphics card hotplug out-of-the-box and rely on proper events sent by the kernel. The framebuffer device is a direct child of the USB interface which is removed immediately after the USB .disconnect() callback. But the fb device in /sys stays around until its final cleanup, at a time where all the parent devices have been removed already. To work around that, we remove the sysfs fb device directly in the USB .disconnect() callback and leave only the cleanup of the internal fb data to the delayed work. Before: add /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2 (usb) add /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.0 (usb) add /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.0/graphics/fb0 (graphics) remove /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.0 (usb) remove /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2 (usb) remove /2-1.2:1.0/graphics/fb0 (graphics) After: add /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2 (usb) add /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.0 (usb) add /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.0/graphics/fb1 (graphics) remove /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.0/graphics/fb1 (graphics) remove /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.0 (usb) remove /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2 (usb) Cc: stable@vger.kernel.org Tested-by: Bernie Thompson Acked-by: Bernie Thompson Signed-off-by: Kay Sievers Signed-off-by: Florian Tobias Schandinat --- drivers/video/fbmem.c | 18 +++++++++++++++++- drivers/video/udlfb.c | 2 +- include/linux/fb.h | 1 + 3 files changed, 19 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c index ac9141b85356..c6ce416ab587 100644 --- a/drivers/video/fbmem.c +++ b/drivers/video/fbmem.c @@ -1665,6 +1665,7 @@ static int do_unregister_framebuffer(struct fb_info *fb_info) if (ret) return -EINVAL; + unlink_framebuffer(fb_info); if (fb_info->pixmap.addr && (fb_info->pixmap.flags & FB_PIXMAP_DEFAULT)) kfree(fb_info->pixmap.addr); @@ -1672,7 +1673,6 @@ static int do_unregister_framebuffer(struct fb_info *fb_info) registered_fb[i] = NULL; num_registered_fb--; fb_cleanup_device(fb_info); - device_destroy(fb_class, MKDEV(FB_MAJOR, i)); event.info = fb_info; fb_notifier_call_chain(FB_EVENT_FB_UNREGISTERED, &event); @@ -1681,6 +1681,22 @@ static int do_unregister_framebuffer(struct fb_info *fb_info) return 0; } +int unlink_framebuffer(struct fb_info *fb_info) +{ + int i; + + i = fb_info->node; + if (i < 0 || i >= FB_MAX || registered_fb[i] != fb_info) + return -EINVAL; + + if (fb_info->dev) { + device_destroy(fb_class, MKDEV(FB_MAJOR, i)); + fb_info->dev = NULL; + } + return 0; +} +EXPORT_SYMBOL(unlink_framebuffer); + void remove_conflicting_framebuffers(struct apertures_struct *a, const char *name, bool primary) { diff --git a/drivers/video/udlfb.c b/drivers/video/udlfb.c index a19773149bd7..a40c05ebbdc2 100644 --- a/drivers/video/udlfb.c +++ b/drivers/video/udlfb.c @@ -1739,7 +1739,7 @@ static void dlfb_usb_disconnect(struct usb_interface *interface) for (i = 0; i < ARRAY_SIZE(fb_device_attrs); i++) device_remove_file(info->dev, &fb_device_attrs[i]); device_remove_bin_file(info->dev, &edid_attr); - + unlink_framebuffer(info); usb_set_intfdata(interface, NULL); /* if clients still have us open, will be freed on last close */ diff --git a/include/linux/fb.h b/include/linux/fb.h index c18122f40543..a395b8c76992 100644 --- a/include/linux/fb.h +++ b/include/linux/fb.h @@ -1003,6 +1003,7 @@ extern ssize_t fb_sys_write(struct fb_info *info, const char __user *buf, /* drivers/video/fbmem.c */ extern int register_framebuffer(struct fb_info *fb_info); extern int unregister_framebuffer(struct fb_info *fb_info); +extern int unlink_framebuffer(struct fb_info *fb_info); extern void remove_conflicting_framebuffers(struct apertures_struct *a, const char *name, bool primary); extern int fb_prepare_logo(struct fb_info *fb_info, int rotate); -- cgit v1.2.3 From c0eb46766d395da8d62148bda2e59bad5e6ee2f2 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 30 Jan 2012 19:56:52 +0000 Subject: regmap: Implement managed regmap_init() Save error handling and unwinding code in drivers by providing managed versions of the regmap init functions, simplifying usage. Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-i2c.c | 17 +++++++++++++++++ drivers/base/regmap/regmap-spi.c | 17 +++++++++++++++++ drivers/base/regmap/regmap.c | 39 +++++++++++++++++++++++++++++++++++++++ include/linux/regmap.h | 8 ++++++++ 4 files changed, 81 insertions(+) (limited to 'include/linux') diff --git a/drivers/base/regmap/regmap-i2c.c b/drivers/base/regmap/regmap-i2c.c index 38621ec87c05..9a3a8c564389 100644 --- a/drivers/base/regmap/regmap-i2c.c +++ b/drivers/base/regmap/regmap-i2c.c @@ -111,4 +111,21 @@ struct regmap *regmap_init_i2c(struct i2c_client *i2c, } EXPORT_SYMBOL_GPL(regmap_init_i2c); +/** + * devm_regmap_init_i2c(): Initialise managed register map + * + * @i2c: Device that will be interacted with + * @config: Configuration for register map + * + * The return value will be an ERR_PTR() on error or a valid pointer + * to a struct regmap. The regmap will be automatically freed by the + * device management code. + */ +struct regmap *devm_regmap_init_i2c(struct i2c_client *i2c, + const struct regmap_config *config) +{ + return devm_regmap_init(&i2c->dev, ®map_i2c, config); +} +EXPORT_SYMBOL_GPL(devm_regmap_init_i2c); + MODULE_LICENSE("GPL"); diff --git a/drivers/base/regmap/regmap-spi.c b/drivers/base/regmap/regmap-spi.c index 2560658de344..7c0c35a39c33 100644 --- a/drivers/base/regmap/regmap-spi.c +++ b/drivers/base/regmap/regmap-spi.c @@ -70,4 +70,21 @@ struct regmap *regmap_init_spi(struct spi_device *spi, } EXPORT_SYMBOL_GPL(regmap_init_spi); +/** + * devm_regmap_init_spi(): Initialise register map + * + * @spi: Device that will be interacted with + * @config: Configuration for register map + * + * The return value will be an ERR_PTR() on error or a valid pointer + * to a struct regmap. The map will be automatically freed by the + * device management code. + */ +struct regmap *devm_regmap_init_spi(struct spi_device *spi, + const struct regmap_config *config) +{ + return devm_regmap_init(&spi->dev, ®map_spi, config); +} +EXPORT_SYMBOL_GPL(devm_regmap_init_spi); + MODULE_LICENSE("GPL"); diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index be10a4ff6609..839cc82bd176 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -258,6 +258,45 @@ err: } EXPORT_SYMBOL_GPL(regmap_init); +static void devm_regmap_release(struct device *dev, void *res) +{ + regmap_exit(*(struct regmap **)res); +} + +/** + * devm_regmap_init(): Initialise managed register map + * + * @dev: Device that will be interacted with + * @bus: Bus-specific callbacks to use with device + * @config: Configuration for register map + * + * The return value will be an ERR_PTR() on error or a valid pointer + * to a struct regmap. This function should generally not be called + * directly, it should be called by bus-specific init functions. The + * map will be automatically freed by the device management code. + */ +struct regmap *devm_regmap_init(struct device *dev, + const struct regmap_bus *bus, + const struct regmap_config *config) +{ + struct regmap **ptr, *regmap; + + ptr = devres_alloc(devm_regmap_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return ERR_PTR(-ENOMEM); + + regmap = regmap_init(dev, bus, config); + if (!IS_ERR(regmap)) { + *ptr = regmap; + devres_add(dev, ptr); + } else { + devres_free(ptr); + } + + return regmap; +} +EXPORT_SYMBOL_GPL(devm_regmap_init); + /** * regmap_reinit_cache(): Reinitialise the current register cache * diff --git a/include/linux/regmap.h b/include/linux/regmap.h index eb93921cdd30..195db51ec79b 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -127,6 +127,14 @@ struct regmap *regmap_init_i2c(struct i2c_client *i2c, struct regmap *regmap_init_spi(struct spi_device *dev, const struct regmap_config *config); +struct regmap *devm_regmap_init(struct device *dev, + const struct regmap_bus *bus, + const struct regmap_config *config); +struct regmap *devm_regmap_init_i2c(struct i2c_client *i2c, + const struct regmap_config *config); +struct regmap *devm_regmap_init_spi(struct spi_device *dev, + const struct regmap_config *config); + void regmap_exit(struct regmap *map); int regmap_reinit_cache(struct regmap *map, const struct regmap_config *config); -- cgit v1.2.3 From 4065d1e7b2164cff4af57b58fac887df2fe75d2a Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Tue, 31 Jan 2012 00:18:00 -0800 Subject: Input: add Cypress TTSP capacitive multi-touch screen support Cypress TrueTouch(tm) Standard Product controllers are found in a wide range of embedded devices. This driver add support for a variety of TTSP controllers. Since the hardware is capable of tracking identifiable contacts, multi-touch protocol type B (stateful) is used to report contact information. The driver is composed of a core driver that process the data sent by the contacts and a set of bus specific interface modules. This patch adds the base core TTSP driver. Signed-off-by: Javier Martinez Canillas Reviewed-by: Henrik Rydberg Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/Kconfig | 32 +- drivers/input/touchscreen/Makefile | 5 +- drivers/input/touchscreen/cyttsp_core.c | 625 ++++++++++++++++++++++++++++++++ drivers/input/touchscreen/cyttsp_core.h | 149 ++++++++ drivers/input/touchscreen/cyttsp_i2c.c | 146 ++++++++ drivers/input/touchscreen/cyttsp_spi.c | 210 +++++++++++ include/linux/input/cyttsp.h | 58 +++ 7 files changed, 1223 insertions(+), 2 deletions(-) create mode 100644 drivers/input/touchscreen/cyttsp_core.c create mode 100644 drivers/input/touchscreen/cyttsp_core.h create mode 100644 drivers/input/touchscreen/cyttsp_i2c.c create mode 100644 drivers/input/touchscreen/cyttsp_spi.c create mode 100644 include/linux/input/cyttsp.h (limited to 'include/linux') diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 4af2a18eb3ba..2b21a7066d36 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -139,7 +139,6 @@ config TOUCHSCREEN_CY8CTMG110 tristate "cy8ctmg110 touchscreen" depends on I2C depends on GPIOLIB - help Say Y here if you have a cy8ctmg110 capacitive touchscreen on an AAVA device. @@ -149,6 +148,37 @@ config TOUCHSCREEN_CY8CTMG110 To compile this driver as a module, choose M here: the module will be called cy8ctmg110_ts. +config TOUCHSCREEN_CYTTSP_CORE + tristate "Cypress TTSP touchscreen" + help + Say Y here if you have a touchscreen using controller from + the Cypress TrueTouch(tm) Standard Product family connected + to your system. You will also need to select appropriate + bus connection below. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called cyttsp_core. + +config TOUCHSCREEN_CYTTSP_I2C + tristate "support I2C bus connection" + depends on TOUCHSCREEN_CYTTSP_CORE && I2C + help + Say Y here if the touchscreen is connected via I2C bus. + + To compile this driver as a module, choose M here: the + module will be called cyttsp_i2c. + +config TOUCHSCREEN_CYTTSP_SPI + tristate "support SPI bus connection" + depends on TOUCHSCREEN_CYTTSP_CORE && SPI_MASTER + help + Say Y here if the touchscreen is connected via SPI bus. + + To compile this driver as a module, choose M here: the + module will be called cyttsp_spi. + config TOUCHSCREEN_DA9034 tristate "Touchscreen support for Dialog Semiconductor DA9034" depends on PMIC_DA903X diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 496091e88460..d9acbdcda3d9 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -16,8 +16,11 @@ obj-$(CONFIG_TOUCHSCREEN_ATMEL_MXT) += atmel_mxt_ts.o obj-$(CONFIG_TOUCHSCREEN_ATMEL_TSADCC) += atmel_tsadcc.o obj-$(CONFIG_TOUCHSCREEN_AUO_PIXCIR) += auo-pixcir-ts.o obj-$(CONFIG_TOUCHSCREEN_BITSY) += h3600_ts_input.o -obj-$(CONFIG_TOUCHSCREEN_BU21013) += bu21013_ts.o +obj-$(CONFIG_TOUCHSCREEN_BU21013) += bu21013_ts.o obj-$(CONFIG_TOUCHSCREEN_CY8CTMG110) += cy8ctmg110_ts.o +obj-$(CONFIG_TOUCHSCREEN_CYTTSP_CORE) += cyttsp_core.o +obj-$(CONFIG_TOUCHSCREEN_CYTTSP_I2C) += cyttsp_i2c.o +obj-$(CONFIG_TOUCHSCREEN_CYTTSP_SPI) += cyttsp_spi.o obj-$(CONFIG_TOUCHSCREEN_DA9034) += da9034-ts.o obj-$(CONFIG_TOUCHSCREEN_DYNAPRO) += dynapro.o obj-$(CONFIG_TOUCHSCREEN_HAMPSHIRE) += hampshire.o diff --git a/drivers/input/touchscreen/cyttsp_core.c b/drivers/input/touchscreen/cyttsp_core.c new file mode 100644 index 000000000000..8be22479b41c --- /dev/null +++ b/drivers/input/touchscreen/cyttsp_core.c @@ -0,0 +1,625 @@ +/* + * Core Source for: + * Cypress TrueTouch(TM) Standard Product (TTSP) touchscreen drivers. + * For use with Cypress Txx3xx parts. + * Supported parts include: + * CY8CTST341 + * CY8CTMA340 + * + * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc. + * Copyright (C) 2012 Javier Martinez Canillas + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, and only version 2, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contact Cypress Semiconductor at www.cypress.com + * + */ + +#include +#include +#include +#include +#include +#include + +#include "cyttsp_core.h" + +/* Bootloader number of command keys */ +#define CY_NUM_BL_KEYS 8 + +/* helpers */ +#define GET_NUM_TOUCHES(x) ((x) & 0x0F) +#define IS_LARGE_AREA(x) (((x) & 0x10) >> 4) +#define IS_BAD_PKT(x) ((x) & 0x20) +#define IS_VALID_APP(x) ((x) & 0x01) +#define IS_OPERATIONAL_ERR(x) ((x) & 0x3F) +#define GET_HSTMODE(reg) (((reg) & 0x70) >> 4) +#define GET_BOOTLOADERMODE(reg) (((reg) & 0x10) >> 4) + +#define CY_REG_BASE 0x00 +#define CY_REG_ACT_DIST 0x1E +#define CY_REG_ACT_INTRVL 0x1D +#define CY_REG_TCH_TMOUT (CY_REG_ACT_INTRVL + 1) +#define CY_REG_LP_INTRVL (CY_REG_TCH_TMOUT + 1) +#define CY_MAXZ 255 +#define CY_DELAY_DFLT 20 /* ms */ +#define CY_DELAY_MAX 500 +#define CY_ACT_DIST_DFLT 0xF8 +#define CY_HNDSHK_BIT 0x80 +/* device mode bits */ +#define CY_OPERATE_MODE 0x00 +#define CY_SYSINFO_MODE 0x10 +/* power mode select bits */ +#define CY_SOFT_RESET_MODE 0x01 /* return to Bootloader mode */ +#define CY_DEEP_SLEEP_MODE 0x02 +#define CY_LOW_POWER_MODE 0x04 + +/* Slots management */ +#define CY_MAX_FINGER 4 +#define CY_MAX_ID 16 + +static const u8 bl_command[] = { + 0x00, /* file offset */ + 0xFF, /* command */ + 0xA5, /* exit bootloader command */ + 0, 1, 2, 3, 4, 5, 6, 7 /* default keys */ +}; + +static int ttsp_read_block_data(struct cyttsp *ts, u8 command, + u8 length, void *buf) +{ + int error; + int tries; + + for (tries = 0; tries < CY_NUM_RETRY; tries++) { + error = ts->bus_ops->read(ts, command, length, buf); + if (!error) + return 0; + + msleep(CY_DELAY_DFLT); + } + + return -EIO; +} + +static int ttsp_write_block_data(struct cyttsp *ts, u8 command, + u8 length, void *buf) +{ + int error; + int tries; + + for (tries = 0; tries < CY_NUM_RETRY; tries++) { + error = ts->bus_ops->write(ts, command, length, buf); + if (!error) + return 0; + + msleep(CY_DELAY_DFLT); + } + + return -EIO; +} + +static int ttsp_send_command(struct cyttsp *ts, u8 cmd) +{ + return ttsp_write_block_data(ts, CY_REG_BASE, sizeof(cmd), &cmd); +} + +static int cyttsp_load_bl_regs(struct cyttsp *ts) +{ + memset(&ts->bl_data, 0, sizeof(ts->bl_data)); + ts->bl_data.bl_status = 0x10; + + return ttsp_read_block_data(ts, CY_REG_BASE, + sizeof(ts->bl_data), &ts->bl_data); +} + +static int cyttsp_exit_bl_mode(struct cyttsp *ts) +{ + int error; + u8 bl_cmd[sizeof(bl_command)]; + + memcpy(bl_cmd, bl_command, sizeof(bl_command)); + if (ts->pdata->bl_keys) + memcpy(&bl_cmd[sizeof(bl_command) - CY_NUM_BL_KEYS], + ts->pdata->bl_keys, sizeof(bl_command)); + + error = ttsp_write_block_data(ts, CY_REG_BASE, + sizeof(bl_cmd), bl_cmd); + if (error) + return error; + + /* wait for TTSP Device to complete the operation */ + msleep(CY_DELAY_DFLT); + + error = cyttsp_load_bl_regs(ts); + if (error) + return error; + + if (GET_BOOTLOADERMODE(ts->bl_data.bl_status)) + return -EIO; + + return 0; +} + +static int cyttsp_set_operational_mode(struct cyttsp *ts) +{ + int error; + + error = ttsp_send_command(ts, CY_OPERATE_MODE); + if (error) + return error; + + /* wait for TTSP Device to complete switch to Operational mode */ + error = ttsp_read_block_data(ts, CY_REG_BASE, + sizeof(ts->xy_data), &ts->xy_data); + if (error) + return error; + + return ts->xy_data.act_dist == CY_ACT_DIST_DFLT ? -EIO : 0; +} + +static int cyttsp_set_sysinfo_mode(struct cyttsp *ts) +{ + int error; + + memset(&ts->sysinfo_data, 0, sizeof(ts->sysinfo_data)); + + /* switch to sysinfo mode */ + error = ttsp_send_command(ts, CY_SYSINFO_MODE); + if (error) + return error; + + /* read sysinfo registers */ + msleep(CY_DELAY_DFLT); + error = ttsp_read_block_data(ts, CY_REG_BASE, sizeof(ts->sysinfo_data), + &ts->sysinfo_data); + if (error) + return error; + + if (!ts->sysinfo_data.tts_verh && !ts->sysinfo_data.tts_verl) + return -EIO; + + return 0; +} + +static int cyttsp_set_sysinfo_regs(struct cyttsp *ts) +{ + int retval = 0; + + if (ts->pdata->act_intrvl != CY_ACT_INTRVL_DFLT || + ts->pdata->tch_tmout != CY_TCH_TMOUT_DFLT || + ts->pdata->lp_intrvl != CY_LP_INTRVL_DFLT) { + + u8 intrvl_ray[] = { + ts->pdata->act_intrvl, + ts->pdata->tch_tmout, + ts->pdata->lp_intrvl + }; + + /* set intrvl registers */ + retval = ttsp_write_block_data(ts, CY_REG_ACT_INTRVL, + sizeof(intrvl_ray), intrvl_ray); + msleep(CY_DELAY_DFLT); + } + + return retval; +} + +static int cyttsp_soft_reset(struct cyttsp *ts) +{ + unsigned long timeout; + int retval; + + /* wait for interrupt to set ready completion */ + INIT_COMPLETION(ts->bl_ready); + ts->state = CY_BL_STATE; + + enable_irq(ts->irq); + + retval = ttsp_send_command(ts, CY_SOFT_RESET_MODE); + if (retval) + goto out; + + timeout = wait_for_completion_timeout(&ts->bl_ready, + msecs_to_jiffies(CY_DELAY_DFLT * CY_DELAY_MAX)); + retval = timeout ? 0 : -EIO; + +out: + ts->state = CY_IDLE_STATE; + disable_irq(ts->irq); + return retval; +} + +static int cyttsp_act_dist_setup(struct cyttsp *ts) +{ + u8 act_dist_setup = ts->pdata->act_dist; + + /* Init gesture; active distance setup */ + return ttsp_write_block_data(ts, CY_REG_ACT_DIST, + sizeof(act_dist_setup), &act_dist_setup); +} + +static void cyttsp_extract_track_ids(struct cyttsp_xydata *xy_data, int *ids) +{ + ids[0] = xy_data->touch12_id >> 4; + ids[1] = xy_data->touch12_id & 0xF; + ids[2] = xy_data->touch34_id >> 4; + ids[3] = xy_data->touch34_id & 0xF; +} + +static const struct cyttsp_tch *cyttsp_get_tch(struct cyttsp_xydata *xy_data, + int idx) +{ + switch (idx) { + case 0: + return &xy_data->tch1; + case 1: + return &xy_data->tch2; + case 2: + return &xy_data->tch3; + case 3: + return &xy_data->tch4; + default: + return NULL; + } +} + +static void cyttsp_report_tchdata(struct cyttsp *ts) +{ + struct cyttsp_xydata *xy_data = &ts->xy_data; + struct input_dev *input = ts->input; + int num_tch = GET_NUM_TOUCHES(xy_data->tt_stat); + const struct cyttsp_tch *tch; + int ids[CY_MAX_ID]; + int i; + DECLARE_BITMAP(used, CY_MAX_ID); + + if (IS_LARGE_AREA(xy_data->tt_stat) == 1) { + /* terminate all active tracks */ + num_tch = 0; + dev_dbg(ts->dev, "%s: Large area detected\n", __func__); + } else if (num_tch > CY_MAX_FINGER) { + /* terminate all active tracks */ + num_tch = 0; + dev_dbg(ts->dev, "%s: Num touch error detected\n", __func__); + } else if (IS_BAD_PKT(xy_data->tt_mode)) { + /* terminate all active tracks */ + num_tch = 0; + dev_dbg(ts->dev, "%s: Invalid buffer detected\n", __func__); + } + + cyttsp_extract_track_ids(xy_data, ids); + + bitmap_zero(used, CY_MAX_ID); + + for (i = 0; i < num_tch; i++) { + tch = cyttsp_get_tch(xy_data, i); + + input_mt_slot(input, ids[i]); + input_mt_report_slot_state(input, MT_TOOL_FINGER, true); + input_report_abs(input, ABS_MT_POSITION_X, be16_to_cpu(tch->x)); + input_report_abs(input, ABS_MT_POSITION_Y, be16_to_cpu(tch->y)); + input_report_abs(input, ABS_MT_TOUCH_MAJOR, tch->z); + + __set_bit(ids[i], used); + } + + for (i = 0; i < CY_MAX_ID; i++) { + if (test_bit(i, used)) + continue; + + input_mt_slot(input, i); + input_mt_report_slot_state(input, MT_TOOL_FINGER, false); + } + + input_sync(input); +} + +static irqreturn_t cyttsp_irq(int irq, void *handle) +{ + struct cyttsp *ts = handle; + int error; + + if (unlikely(ts->state == CY_BL_STATE)) { + complete(&ts->bl_ready); + goto out; + } + + /* Get touch data from CYTTSP device */ + error = ttsp_read_block_data(ts, CY_REG_BASE, + sizeof(struct cyttsp_xydata), &ts->xy_data); + if (error) + goto out; + + /* provide flow control handshake */ + if (ts->pdata->use_hndshk) { + error = ttsp_send_command(ts, + ts->xy_data.hst_mode ^ CY_HNDSHK_BIT); + if (error) + goto out; + } + + if (unlikely(ts->state == CY_IDLE_STATE)) + goto out; + + if (GET_BOOTLOADERMODE(ts->xy_data.tt_mode)) { + /* + * TTSP device has reset back to bootloader mode. + * Restore to operational mode. + */ + error = cyttsp_exit_bl_mode(ts); + if (error) { + dev_err(ts->dev, + "Could not return to operational mode, err: %d\n", + error); + ts->state = CY_IDLE_STATE; + } + } else { + cyttsp_report_tchdata(ts); + } + +out: + return IRQ_HANDLED; +} + +static int cyttsp_power_on(struct cyttsp *ts) +{ + int error; + + error = cyttsp_soft_reset(ts); + if (error) + return error; + + error = cyttsp_load_bl_regs(ts); + if (error) + return error; + + if (GET_BOOTLOADERMODE(ts->bl_data.bl_status) && + IS_VALID_APP(ts->bl_data.bl_status)) { + error = cyttsp_exit_bl_mode(ts); + if (error) + return error; + } + + if (GET_HSTMODE(ts->bl_data.bl_file) != CY_OPERATE_MODE || + IS_OPERATIONAL_ERR(ts->bl_data.bl_status)) { + return -ENODEV; + } + + error = cyttsp_set_sysinfo_mode(ts); + if (error) + return error; + + error = cyttsp_set_sysinfo_regs(ts); + if (error) + return error; + + error = cyttsp_set_operational_mode(ts); + if (error) + return error; + + /* init active distance */ + error = cyttsp_act_dist_setup(ts); + if (error) + return error; + + ts->state = CY_ACTIVE_STATE; + + return 0; +} + +static int cyttsp_enable(struct cyttsp *ts) +{ + int error; + + /* + * The device firmware can wake on an I2C or SPI memory slave + * address match. So just reading a register is sufficient to + * wake up the device. The first read attempt will fail but it + * will wake it up making the second read attempt successful. + */ + error = ttsp_read_block_data(ts, CY_REG_BASE, + sizeof(ts->xy_data), &ts->xy_data); + if (error) + return error; + + if (GET_HSTMODE(ts->xy_data.hst_mode)) + return -EIO; + + enable_irq(ts->irq); + + return 0; +} + +static int cyttsp_disable(struct cyttsp *ts) +{ + int error; + + error = ttsp_send_command(ts, CY_LOW_POWER_MODE); + if (error) + return error; + + disable_irq(ts->irq); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int cyttsp_suspend(struct device *dev) +{ + struct cyttsp *ts = dev_get_drvdata(dev); + int retval = 0; + + mutex_lock(&ts->input->mutex); + + if (ts->input->users) { + retval = cyttsp_disable(ts); + if (retval == 0) + ts->suspended = true; + } + + mutex_unlock(&ts->input->mutex); + + return retval; +} + +static int cyttsp_resume(struct device *dev) +{ + struct cyttsp *ts = dev_get_drvdata(dev); + + mutex_lock(&ts->input->mutex); + + if (ts->input->users) + cyttsp_enable(ts); + + ts->suspended = false; + + mutex_unlock(&ts->input->mutex); + + return 0; +} + +#endif + +SIMPLE_DEV_PM_OPS(cyttsp_pm_ops, cyttsp_suspend, cyttsp_resume); +EXPORT_SYMBOL_GPL(cyttsp_pm_ops); + +static int cyttsp_open(struct input_dev *dev) +{ + struct cyttsp *ts = input_get_drvdata(dev); + int retval = 0; + + if (!ts->suspended) + retval = cyttsp_enable(ts); + + return retval; +} + +static void cyttsp_close(struct input_dev *dev) +{ + struct cyttsp *ts = input_get_drvdata(dev); + + if (!ts->suspended) + cyttsp_disable(ts); +} + +struct cyttsp *cyttsp_probe(const struct cyttsp_bus_ops *bus_ops, + struct device *dev, int irq, size_t xfer_buf_size) +{ + const struct cyttsp_platform_data *pdata = dev->platform_data; + struct cyttsp *ts; + struct input_dev *input_dev; + int error; + + if (!dev || !bus_ops || !pdata || !pdata->name || irq <= 0) { + error = -EINVAL; + goto err_out; + } + + ts = kzalloc(sizeof(*ts) + xfer_buf_size, GFP_KERNEL); + input_dev = input_allocate_device(); + if (!ts || !input_dev) { + error = -ENOMEM; + goto err_free_mem; + } + + ts->dev = dev; + ts->input = input_dev; + ts->pdata = dev->platform_data; + ts->bus_ops = bus_ops; + ts->irq = irq; + + init_completion(&ts->bl_ready); + snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(dev)); + + if (pdata->init) { + error = pdata->init(); + if (error) { + dev_err(ts->dev, "platform init failed, err: %d\n", + error); + goto err_free_mem; + } + } + + input_dev->name = pdata->name; + input_dev->phys = ts->phys; + input_dev->id.bustype = bus_ops->bustype; + input_dev->dev.parent = ts->dev; + + input_dev->open = cyttsp_open; + input_dev->close = cyttsp_close; + + input_set_drvdata(input_dev, ts); + + __set_bit(EV_ABS, input_dev->evbit); + input_set_abs_params(input_dev, ABS_MT_POSITION_X, + 0, pdata->maxx, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, + 0, pdata->maxy, 0, 0); + input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, + 0, CY_MAXZ, 0, 0); + + input_mt_init_slots(input_dev, CY_MAX_ID); + + error = request_threaded_irq(ts->irq, NULL, cyttsp_irq, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + pdata->name, ts); + if (error) { + dev_err(ts->dev, "failed to request IRQ %d, err: %d\n", + ts->irq, error); + goto err_platform_exit; + } + + disable_irq(ts->irq); + + error = cyttsp_power_on(ts); + if (error) + goto err_free_irq; + + error = input_register_device(input_dev); + if (error) { + dev_err(ts->dev, "failed to register input device: %d\n", + error); + goto err_free_irq; + } + + return ts; + +err_free_irq: + free_irq(ts->irq, ts); +err_platform_exit: + if (pdata->exit) + pdata->exit(); +err_free_mem: + input_free_device(input_dev); + kfree(ts); +err_out: + return ERR_PTR(error); +} +EXPORT_SYMBOL_GPL(cyttsp_probe); + +void cyttsp_remove(struct cyttsp *ts) +{ + free_irq(ts->irq, ts); + input_unregister_device(ts->input); + if (ts->pdata->exit) + ts->pdata->exit(); + kfree(ts); +} +EXPORT_SYMBOL_GPL(cyttsp_remove); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard touchscreen driver core"); +MODULE_AUTHOR("Cypress"); diff --git a/drivers/input/touchscreen/cyttsp_core.h b/drivers/input/touchscreen/cyttsp_core.h new file mode 100644 index 000000000000..1aa3c6967e70 --- /dev/null +++ b/drivers/input/touchscreen/cyttsp_core.h @@ -0,0 +1,149 @@ +/* + * Header file for: + * Cypress TrueTouch(TM) Standard Product (TTSP) touchscreen drivers. + * For use with Cypress Txx3xx parts. + * Supported parts include: + * CY8CTST341 + * CY8CTMA340 + * + * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc. + * Copyright (C) 2012 Javier Martinez Canillas + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, and only version 2, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contact Cypress Semiconductor at www.cypress.com + * + */ + + +#ifndef __CYTTSP_CORE_H__ +#define __CYTTSP_CORE_H__ + +#include +#include +#include +#include +#include +#include + +#define CY_NUM_RETRY 16 /* max number of retries for read ops */ + +struct cyttsp_tch { + __be16 x, y; + u8 z; +} __packed; + +/* TrueTouch Standard Product Gen3 interface definition */ +struct cyttsp_xydata { + u8 hst_mode; + u8 tt_mode; + u8 tt_stat; + struct cyttsp_tch tch1; + u8 touch12_id; + struct cyttsp_tch tch2; + u8 gest_cnt; + u8 gest_id; + struct cyttsp_tch tch3; + u8 touch34_id; + struct cyttsp_tch tch4; + u8 tt_undef[3]; + u8 act_dist; + u8 tt_reserved; +} __packed; + + +/* TTSP System Information interface definition */ +struct cyttsp_sysinfo_data { + u8 hst_mode; + u8 mfg_cmd; + u8 mfg_stat; + u8 cid[3]; + u8 tt_undef1; + u8 uid[8]; + u8 bl_verh; + u8 bl_verl; + u8 tts_verh; + u8 tts_verl; + u8 app_idh; + u8 app_idl; + u8 app_verh; + u8 app_verl; + u8 tt_undef[5]; + u8 scn_typ; + u8 act_intrvl; + u8 tch_tmout; + u8 lp_intrvl; +}; + +/* TTSP Bootloader Register Map interface definition */ +#define CY_BL_CHKSUM_OK 0x01 +struct cyttsp_bootloader_data { + u8 bl_file; + u8 bl_status; + u8 bl_error; + u8 blver_hi; + u8 blver_lo; + u8 bld_blver_hi; + u8 bld_blver_lo; + u8 ttspver_hi; + u8 ttspver_lo; + u8 appid_hi; + u8 appid_lo; + u8 appver_hi; + u8 appver_lo; + u8 cid_0; + u8 cid_1; + u8 cid_2; +}; + +struct cyttsp; + +struct cyttsp_bus_ops { + u16 bustype; + int (*write)(struct cyttsp *ts, + u8 addr, u8 length, const void *values); + int (*read)(struct cyttsp *ts, u8 addr, u8 length, void *values); +}; + +enum cyttsp_state { + CY_IDLE_STATE, + CY_ACTIVE_STATE, + CY_BL_STATE, +}; + +struct cyttsp { + struct device *dev; + int irq; + struct input_dev *input; + char phys[32]; + const struct cyttsp_platform_data *pdata; + const struct cyttsp_bus_ops *bus_ops; + struct cyttsp_bootloader_data bl_data; + struct cyttsp_sysinfo_data sysinfo_data; + struct cyttsp_xydata xy_data; + struct completion bl_ready; + enum cyttsp_state state; + bool suspended; + + u8 xfer_buf[] ____cacheline_aligned; +}; + +struct cyttsp *cyttsp_probe(const struct cyttsp_bus_ops *bus_ops, + struct device *dev, int irq, size_t xfer_buf_size); +void cyttsp_remove(struct cyttsp *ts); + +extern const struct dev_pm_ops cyttsp_pm_ops; + +#endif /* __CYTTSP_CORE_H__ */ diff --git a/drivers/input/touchscreen/cyttsp_i2c.c b/drivers/input/touchscreen/cyttsp_i2c.c new file mode 100644 index 000000000000..c7110cc06b9d --- /dev/null +++ b/drivers/input/touchscreen/cyttsp_i2c.c @@ -0,0 +1,146 @@ +/* + * Source for: + * Cypress TrueTouch(TM) Standard Product (TTSP) I2C touchscreen driver. + * For use with Cypress Txx3xx parts. + * Supported parts include: + * CY8CTST341 + * CY8CTMA340 + * + * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc. + * Copyright (C) 2012 Javier Martinez Canillas + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, and only version 2, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contact Cypress Semiconductor at www.cypress.com + * + */ + +#include "cyttsp_core.h" + +#include +#include + +#define CY_I2C_DATA_SIZE 128 + +static int cyttsp_i2c_read_block_data(struct cyttsp *ts, + u8 addr, u8 length, void *values) +{ + struct i2c_client *client = to_i2c_client(ts->dev); + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = 0, + .len = 1, + .buf = &addr, + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = length, + .buf = values, + }, + }; + int retval; + + retval = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (retval < 0) + return retval; + + return retval != ARRAY_SIZE(msgs) ? -EIO : 0; +} + +static int cyttsp_i2c_write_block_data(struct cyttsp *ts, + u8 addr, u8 length, const void *values) +{ + struct i2c_client *client = to_i2c_client(ts->dev); + int retval; + + ts->xfer_buf[0] = addr; + memcpy(&ts->xfer_buf[1], values, length); + + retval = i2c_master_send(client, ts->xfer_buf, length + 1); + + return retval < 0 ? retval : 0; +} + +static const struct cyttsp_bus_ops cyttsp_i2c_bus_ops = { + .bustype = BUS_I2C, + .write = cyttsp_i2c_write_block_data, + .read = cyttsp_i2c_read_block_data, +}; + +static int __devinit cyttsp_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct cyttsp *ts; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, "I2C functionality not Supported\n"); + return -EIO; + } + + ts = cyttsp_probe(&cyttsp_i2c_bus_ops, &client->dev, client->irq, + CY_I2C_DATA_SIZE); + + if (IS_ERR(ts)) + return PTR_ERR(ts); + + i2c_set_clientdata(client, ts); + + return 0; +} + +static int __devexit cyttsp_i2c_remove(struct i2c_client *client) +{ + struct cyttsp *ts = i2c_get_clientdata(client); + + cyttsp_remove(ts); + + return 0; +} + +static const struct i2c_device_id cyttsp_i2c_id[] = { + { CY_I2C_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, cyttsp_i2c_id); + +static struct i2c_driver cyttsp_i2c_driver = { + .driver = { + .name = CY_I2C_NAME, + .owner = THIS_MODULE, + .pm = &cyttsp_pm_ops, + }, + .probe = cyttsp_i2c_probe, + .remove = __devexit_p(cyttsp_i2c_remove), + .id_table = cyttsp_i2c_id, +}; + +static int __init cyttsp_i2c_init(void) +{ + return i2c_add_driver(&cyttsp_i2c_driver); +} +module_init(cyttsp_i2c_init); + +static void __exit cyttsp_i2c_exit(void) +{ + return i2c_del_driver(&cyttsp_i2c_driver); +} +module_exit(cyttsp_i2c_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard Product (TTSP) I2C driver"); +MODULE_AUTHOR("Cypress"); +MODULE_ALIAS("i2c:cyttsp"); diff --git a/drivers/input/touchscreen/cyttsp_spi.c b/drivers/input/touchscreen/cyttsp_spi.c new file mode 100644 index 000000000000..9db5f8754d10 --- /dev/null +++ b/drivers/input/touchscreen/cyttsp_spi.c @@ -0,0 +1,210 @@ +/* + * Source for: + * Cypress TrueTouch(TM) Standard Product (TTSP) SPI touchscreen driver. + * For use with Cypress Txx3xx parts. + * Supported parts include: + * CY8CTST341 + * CY8CTMA340 + * + * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc. + * Copyright (C) 2012 Javier Martinez Canillas + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, and only version 2, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contact Cypress Semiconductor at www.cypress.com + * + */ + +#include "cyttsp_core.h" + +#include +#include +#include + +#define CY_SPI_WR_OP 0x00 /* r/~w */ +#define CY_SPI_RD_OP 0x01 +#define CY_SPI_CMD_BYTES 4 +#define CY_SPI_SYNC_BYTE 2 +#define CY_SPI_SYNC_ACK1 0x62 /* from protocol v.2 */ +#define CY_SPI_SYNC_ACK2 0x9D /* from protocol v.2 */ +#define CY_SPI_DATA_SIZE 128 +#define CY_SPI_DATA_BUF_SIZE (CY_SPI_CMD_BYTES + CY_SPI_DATA_SIZE) +#define CY_SPI_BITS_PER_WORD 8 + +static int cyttsp_spi_xfer(struct cyttsp *ts, + u8 op, u8 reg, u8 *buf, int length) +{ + struct spi_device *spi = to_spi_device(ts->dev); + struct spi_message msg; + struct spi_transfer xfer[2]; + u8 *wr_buf = &ts->xfer_buf[0]; + u8 *rd_buf = &ts->xfer_buf[CY_SPI_DATA_BUF_SIZE]; + int retval; + int i; + + if (length > CY_SPI_DATA_SIZE) { + dev_err(ts->dev, "%s: length %d is too big.\n", + __func__, length); + return -EINVAL; + } + + memset(wr_buf, 0, CY_SPI_DATA_BUF_SIZE); + memset(rd_buf, 0, CY_SPI_DATA_BUF_SIZE); + + wr_buf[0] = 0x00; /* header byte 0 */ + wr_buf[1] = 0xFF; /* header byte 1 */ + wr_buf[2] = reg; /* reg index */ + wr_buf[3] = op; /* r/~w */ + if (op == CY_SPI_WR_OP) + memcpy(wr_buf + CY_SPI_CMD_BYTES, buf, length); + + memset(xfer, 0, sizeof(xfer)); + spi_message_init(&msg); + + /* + We set both TX and RX buffers because Cypress TTSP + requires full duplex operation. + */ + xfer[0].tx_buf = wr_buf; + xfer[0].rx_buf = rd_buf; + switch (op) { + case CY_SPI_WR_OP: + xfer[0].len = length + CY_SPI_CMD_BYTES; + spi_message_add_tail(&xfer[0], &msg); + break; + + case CY_SPI_RD_OP: + xfer[0].len = CY_SPI_CMD_BYTES; + spi_message_add_tail(&xfer[0], &msg); + + xfer[1].rx_buf = buf; + xfer[1].len = length; + spi_message_add_tail(&xfer[1], &msg); + break; + + default: + dev_err(ts->dev, "%s: bad operation code=%d\n", __func__, op); + return -EINVAL; + } + + retval = spi_sync(spi, &msg); + if (retval < 0) { + dev_dbg(ts->dev, "%s: spi_sync() error %d, len=%d, op=%d\n", + __func__, retval, xfer[1].len, op); + + /* + * do not return here since was a bad ACK sequence + * let the following ACK check handle any errors and + * allow silent retries + */ + } + + if (rd_buf[CY_SPI_SYNC_BYTE] != CY_SPI_SYNC_ACK1 || + rd_buf[CY_SPI_SYNC_BYTE + 1] != CY_SPI_SYNC_ACK2) { + + dev_dbg(ts->dev, "%s: operation %d failed\n", __func__, op); + + for (i = 0; i < CY_SPI_CMD_BYTES; i++) + dev_dbg(ts->dev, "%s: test rd_buf[%d]:0x%02x\n", + __func__, i, rd_buf[i]); + for (i = 0; i < length; i++) + dev_dbg(ts->dev, "%s: test buf[%d]:0x%02x\n", + __func__, i, buf[i]); + + return -EIO; + } + + return 0; +} + +static int cyttsp_spi_read_block_data(struct cyttsp *ts, + u8 addr, u8 length, void *data) +{ + return cyttsp_spi_xfer(ts, CY_SPI_RD_OP, addr, data, length); +} + +static int cyttsp_spi_write_block_data(struct cyttsp *ts, + u8 addr, u8 length, const void *data) +{ + return cyttsp_spi_xfer(ts, CY_SPI_WR_OP, addr, (void *)data, length); +} + +static const struct cyttsp_bus_ops cyttsp_spi_bus_ops = { + .bustype = BUS_SPI, + .write = cyttsp_spi_write_block_data, + .read = cyttsp_spi_read_block_data, +}; + +static int __devinit cyttsp_spi_probe(struct spi_device *spi) +{ + struct cyttsp *ts; + int error; + + /* Set up SPI*/ + spi->bits_per_word = CY_SPI_BITS_PER_WORD; + spi->mode = SPI_MODE_0; + error = spi_setup(spi); + if (error < 0) { + dev_err(&spi->dev, "%s: SPI setup error %d\n", + __func__, error); + return error; + } + + ts = cyttsp_probe(&cyttsp_spi_bus_ops, &spi->dev, spi->irq, + CY_SPI_DATA_BUF_SIZE * 2); + if (IS_ERR(ts)) + return PTR_ERR(ts); + + spi_set_drvdata(spi, ts); + + return 0; +} + +static int __devexit cyttsp_spi_remove(struct spi_device *spi) +{ + struct cyttsp *ts = spi_get_drvdata(spi); + + cyttsp_remove(ts); + + return 0; +} + +static struct spi_driver cyttsp_spi_driver = { + .driver = { + .name = CY_SPI_NAME, + .owner = THIS_MODULE, + .pm = &cyttsp_pm_ops, + }, + .probe = cyttsp_spi_probe, + .remove = __devexit_p(cyttsp_spi_remove), +}; + +static int __init cyttsp_spi_init(void) +{ + return spi_register_driver(&cyttsp_spi_driver); +} +module_init(cyttsp_spi_init); + +static void __exit cyttsp_spi_exit(void) +{ + spi_unregister_driver(&cyttsp_spi_driver); +} +module_exit(cyttsp_spi_exit); + +MODULE_ALIAS("spi:cyttsp"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard Product (TTSP) SPI driver"); +MODULE_AUTHOR("Cypress"); +MODULE_ALIAS("spi:cyttsp"); diff --git a/include/linux/input/cyttsp.h b/include/linux/input/cyttsp.h new file mode 100644 index 000000000000..5af7c66f1fca --- /dev/null +++ b/include/linux/input/cyttsp.h @@ -0,0 +1,58 @@ +/* + * Header file for: + * Cypress TrueTouch(TM) Standard Product (TTSP) touchscreen drivers. + * For use with Cypress Txx3xx parts. + * Supported parts include: + * CY8CTST341 + * CY8CTMA340 + * + * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc. + * Copyright (C) 2012 Javier Martinez Canillas + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, and only version 2, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contact Cypress Semiconductor at www.cypress.com (kev@cypress.com) + * + */ +#ifndef _CYTTSP_H_ +#define _CYTTSP_H_ + +#define CY_SPI_NAME "cyttsp-spi" +#define CY_I2C_NAME "cyttsp-i2c" +/* Active Power state scanning/processing refresh interval */ +#define CY_ACT_INTRVL_DFLT 0x00 /* ms */ +/* touch timeout for the Active power */ +#define CY_TCH_TMOUT_DFLT 0xFF /* ms */ +/* Low Power state scanning/processing refresh interval */ +#define CY_LP_INTRVL_DFLT 0x0A /* ms */ +/* Active distance in pixels for a gesture to be reported */ +#define CY_ACT_DIST_DFLT 0xF8 /* pixels */ + +struct cyttsp_platform_data { + u32 maxx; + u32 maxy; + bool use_hndshk; + u8 act_dist; /* Active distance */ + u8 act_intrvl; /* Active refresh interval; ms */ + u8 tch_tmout; /* Active touch timeout; ms */ + u8 lp_intrvl; /* Low power refresh interval; ms */ + int (*init)(void); + void (*exit)(void); + char *name; + s16 irq_gpio; + u8 *bl_keys; +}; + +#endif /* _CYTTSP_H_ */ -- cgit v1.2.3 From f35ef7cab2db0a8b577bef59122b59220efdac44 Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Tue, 31 Jan 2012 20:06:07 +0900 Subject: ARM: S3C24XX: move spi-s3c24xx platdata out of mach spi.h now only contains the definition of the platform data structure for the driver in spi-s3c24xx.c . Therefore it does not need to stay in include/mach but can instead live in linux/spi/s3c24xx.h . Signed-off-by: Heiko Stuebner Cc: Grant Likely Signed-off-by: Kukjin Kim --- arch/arm/mach-s3c2410/include/mach/spi.h | 27 --------------------------- arch/arm/mach-s3c2440/mach-gta02.c | 2 +- drivers/spi/spi-s3c24xx.c | 2 +- include/linux/spi/s3c24xx.h | 26 ++++++++++++++++++++++++++ 4 files changed, 28 insertions(+), 29 deletions(-) delete mode 100644 arch/arm/mach-s3c2410/include/mach/spi.h create mode 100644 include/linux/spi/s3c24xx.h (limited to 'include/linux') diff --git a/arch/arm/mach-s3c2410/include/mach/spi.h b/arch/arm/mach-s3c2410/include/mach/spi.h deleted file mode 100644 index 2a686c0751ce..000000000000 --- a/arch/arm/mach-s3c2410/include/mach/spi.h +++ /dev/null @@ -1,27 +0,0 @@ -/* arch/arm/mach-s3c2410/include/mach/spi.h - * - * Copyright (c) 2006 Simtec Electronics - * Ben Dooks - * - * S3C2410 - SPI Controller platform_device info - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. -*/ - -#ifndef __ASM_ARCH_SPI_H -#define __ASM_ARCH_SPI_H __FILE__ - -struct s3c2410_spi_info { - int pin_cs; /* simple gpio cs */ - unsigned int num_cs; /* total chipselects */ - int bus_num; /* bus number to use. */ - - unsigned int use_fiq:1; /* use fiq */ - - void (*gpio_setup)(struct s3c2410_spi_info *spi, int enable); - void (*set_cs)(struct s3c2410_spi_info *spi, int cs, int pol); -}; - -#endif /* __ASM_ARCH_SPI_H */ diff --git a/arch/arm/mach-s3c2440/mach-gta02.c b/arch/arm/mach-s3c2440/mach-gta02.c index 5859e609d28c..cf270f51d149 100644 --- a/arch/arm/mach-s3c2440/mach-gta02.c +++ b/arch/arm/mach-s3c2440/mach-gta02.c @@ -38,6 +38,7 @@ #include #include #include +#include #include @@ -73,7 +74,6 @@ #include #include -#include #include #include #include diff --git a/drivers/spi/spi-s3c24xx.c b/drivers/spi/spi-s3c24xx.c index fc064535f4fc..8ee7d790ce49 100644 --- a/drivers/spi/spi-s3c24xx.c +++ b/drivers/spi/spi-s3c24xx.c @@ -24,10 +24,10 @@ #include #include +#include #include #include -#include #include #include diff --git a/include/linux/spi/s3c24xx.h b/include/linux/spi/s3c24xx.h new file mode 100644 index 000000000000..c23b923e493b --- /dev/null +++ b/include/linux/spi/s3c24xx.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2006 Simtec Electronics + * Ben Dooks + * + * S3C2410 - SPI Controller platform_device info + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#ifndef __LINUX_SPI_S3C24XX_H +#define __LINUX_SPI_S3C24XX_H __FILE__ + +struct s3c2410_spi_info { + int pin_cs; /* simple gpio cs */ + unsigned int num_cs; /* total chipselects */ + int bus_num; /* bus number to use. */ + + unsigned int use_fiq:1; /* use fiq */ + + void (*gpio_setup)(struct s3c2410_spi_info *spi, int enable); + void (*set_cs)(struct s3c2410_spi_info *spi, int cs, int pol); +}; + +#endif /* __LINUX_SPI_S3C24XX_H */ -- cgit v1.2.3 From 5bff0386305461021bbef2d958fa0f0151f56a6f Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Tue, 8 Nov 2011 15:09:19 +0300 Subject: SUNRPC: remove non-exclusive pipe creation from RPC pipefs This patch-set was created in context of clone of git branch: git://git.linux-nfs.org/projects/trondmy/nfs-2.6.git. v2: 1) Rebased of current repo state (i.e. all commits were pulled before apply) I feel it is ready for inclusion if no objections will appear. SUNRPC pipefs non-exclusive pipe creation code looks obsolete. IOW, as I see it, all pipes are creating with unique full path and only once. Signed-off-by: Stanislav Kinsbursky Signed-off-by: Trond Myklebust --- include/linux/sunrpc/rpc_pipe_fs.h | 1 - net/sunrpc/rpc_pipe.c | 44 ++++++-------------------------------- 2 files changed, 6 insertions(+), 39 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sunrpc/rpc_pipe_fs.h b/include/linux/sunrpc/rpc_pipe_fs.h index 2bb03d77375a..edadc3acf949 100644 --- a/include/linux/sunrpc/rpc_pipe_fs.h +++ b/include/linux/sunrpc/rpc_pipe_fs.h @@ -30,7 +30,6 @@ struct rpc_inode { int pipelen; int nreaders; int nwriters; - int nkern_readwriters; wait_queue_head_t waitq; #define RPC_PIPE_WAIT_FOR_OPEN 1 int flags; diff --git a/net/sunrpc/rpc_pipe.c b/net/sunrpc/rpc_pipe.c index 63a7a7add21e..8e3397fc0a7d 100644 --- a/net/sunrpc/rpc_pipe.c +++ b/net/sunrpc/rpc_pipe.c @@ -554,7 +554,6 @@ static int __rpc_mkpipe(struct inode *dir, struct dentry *dentry, if (err) return err; rpci = RPC_I(dentry->d_inode); - rpci->nkern_readwriters = 1; rpci->private = private; rpci->flags = flags; rpci->ops = ops; @@ -587,16 +586,12 @@ static int __rpc_unlink(struct inode *dir, struct dentry *dentry) static int __rpc_rmpipe(struct inode *dir, struct dentry *dentry) { struct inode *inode = dentry->d_inode; - struct rpc_inode *rpci = RPC_I(inode); - rpci->nkern_readwriters--; - if (rpci->nkern_readwriters != 0) - return 0; rpc_close_pipes(inode); return __rpc_unlink(dir, dentry); } -static struct dentry *__rpc_lookup_create(struct dentry *parent, +static struct dentry *__rpc_lookup_create_exclusive(struct dentry *parent, struct qstr *name) { struct dentry *dentry; @@ -604,27 +599,13 @@ static struct dentry *__rpc_lookup_create(struct dentry *parent, dentry = d_lookup(parent, name); if (!dentry) { dentry = d_alloc(parent, name); - if (!dentry) { - dentry = ERR_PTR(-ENOMEM); - goto out_err; - } + if (!dentry) + return ERR_PTR(-ENOMEM); } - if (!dentry->d_inode) + if (dentry->d_inode == NULL) { d_set_d_op(dentry, &rpc_dentry_operations); -out_err: - return dentry; -} - -static struct dentry *__rpc_lookup_create_exclusive(struct dentry *parent, - struct qstr *name) -{ - struct dentry *dentry; - - dentry = __rpc_lookup_create(parent, name); - if (IS_ERR(dentry)) - return dentry; - if (dentry->d_inode == NULL) return dentry; + } dput(dentry); return ERR_PTR(-EEXIST); } @@ -812,22 +793,9 @@ struct dentry *rpc_mkpipe(struct dentry *parent, const char *name, q.hash = full_name_hash(q.name, q.len), mutex_lock_nested(&dir->i_mutex, I_MUTEX_PARENT); - dentry = __rpc_lookup_create(parent, &q); + dentry = __rpc_lookup_create_exclusive(parent, &q); if (IS_ERR(dentry)) goto out; - if (dentry->d_inode) { - struct rpc_inode *rpci = RPC_I(dentry->d_inode); - if (rpci->private != private || - rpci->ops != ops || - rpci->flags != flags) { - dput (dentry); - err = -EBUSY; - goto out_err; - } - rpci->nkern_readwriters++; - goto out; - } - err = __rpc_mkpipe(dir, dentry, umode, &rpc_pipe_fops, private, ops, flags); if (err) -- cgit v1.2.3 From 2d00131acc641b2cb6f0bdefb8c7bdd8fdf7410b Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Mon, 26 Dec 2011 15:39:13 +0300 Subject: SUNRPC: send notification events on pipefs sb creation and destruction They will be used to notify subscribers about pipefs superblock creation and destruction. Subcribers will have to create their dentries on passed superblock on mount event and destroy otherwise. Signed-off-by: Stanislav Kinsbursky Signed-off-by: Trond Myklebust --- include/linux/sunrpc/rpc_pipe_fs.h | 8 ++++++++ net/sunrpc/rpc_pipe.c | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) (limited to 'include/linux') diff --git a/include/linux/sunrpc/rpc_pipe_fs.h b/include/linux/sunrpc/rpc_pipe_fs.h index edadc3acf949..d39782ce6c67 100644 --- a/include/linux/sunrpc/rpc_pipe_fs.h +++ b/include/linux/sunrpc/rpc_pipe_fs.h @@ -43,6 +43,14 @@ RPC_I(struct inode *inode) return container_of(inode, struct rpc_inode, vfs_inode); } +extern int rpc_pipefs_notifier_register(struct notifier_block *); +extern void rpc_pipefs_notifier_unregister(struct notifier_block *); + +enum { + RPC_PIPEFS_MOUNT, + RPC_PIPEFS_UMOUNT, +}; + extern ssize_t rpc_pipe_generic_upcall(struct file *, struct rpc_pipe_msg *, char __user *, size_t); extern int rpc_queue_upcall(struct inode *, struct rpc_pipe_msg *); diff --git a/net/sunrpc/rpc_pipe.c b/net/sunrpc/rpc_pipe.c index f628b0f48a87..58a5062df260 100644 --- a/net/sunrpc/rpc_pipe.c +++ b/net/sunrpc/rpc_pipe.c @@ -28,8 +28,10 @@ #include #include #include +#include #include "netns.h" +#include "sunrpc.h" static struct vfsmount *rpc_mnt __read_mostly; static int rpc_mount_count; @@ -41,6 +43,20 @@ static struct kmem_cache *rpc_inode_cachep __read_mostly; #define RPC_UPCALL_TIMEOUT (30*HZ) +static BLOCKING_NOTIFIER_HEAD(rpc_pipefs_notifier_list); + +int rpc_pipefs_notifier_register(struct notifier_block *nb) +{ + return blocking_notifier_chain_cond_register(&rpc_pipefs_notifier_list, nb); +} +EXPORT_SYMBOL_GPL(rpc_pipefs_notifier_register); + +void rpc_pipefs_notifier_unregister(struct notifier_block *nb) +{ + blocking_notifier_chain_unregister(&rpc_pipefs_notifier_list, nb); +} +EXPORT_SYMBOL_GPL(rpc_pipefs_notifier_unregister); + static void rpc_purge_list(struct rpc_inode *rpci, struct list_head *head, void (*destroy_msg)(struct rpc_pipe_msg *), int err) { @@ -997,6 +1013,7 @@ rpc_fill_super(struct super_block *sb, void *data, int silent) struct inode *inode; struct dentry *root; struct net *net = data; + int err; sb->s_blocksize = PAGE_CACHE_SIZE; sb->s_blocksize_bits = PAGE_CACHE_SHIFT; @@ -1014,8 +1031,20 @@ rpc_fill_super(struct super_block *sb, void *data, int silent) } if (rpc_populate(root, files, RPCAUTH_lockd, RPCAUTH_RootEOF, NULL)) return -ENOMEM; + err = blocking_notifier_call_chain(&rpc_pipefs_notifier_list, + RPC_PIPEFS_MOUNT, + sb); + if (err) + goto err_depopulate; sb->s_fs_info = get_net(net); return 0; + +err_depopulate: + blocking_notifier_call_chain(&rpc_pipefs_notifier_list, + RPC_PIPEFS_UMOUNT, + sb); + __rpc_depopulate(root, files, RPCAUTH_lockd, RPCAUTH_RootEOF); + return err; } static struct dentry * @@ -1030,6 +1059,9 @@ void rpc_kill_sb(struct super_block *sb) struct net *net = sb->s_fs_info; put_net(net); + blocking_notifier_call_chain(&rpc_pipefs_notifier_list, + RPC_PIPEFS_UMOUNT, + sb); kill_litter_super(sb); } -- cgit v1.2.3 From 432eb1a5fb380477ae759041bac2bb305977e436 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Mon, 26 Dec 2011 15:39:22 +0300 Subject: SUNRPC: pipefs dentry lookup helper introduced In all places, where pipefs dentries are created, only directory inode is actually required to create new dentry. And all this directories has root pipefs dentry as their parent. So we actually don't need this pipefs mount point at all if some pipefs lookup method will be provided. IOW, all we really need is just superblock and simple lookup method to find root's child dentry with appropriate name. And this patch introduces this method. Note, that no locking implemented in rpc_d_lookup_sb(). So it can be used only in case of assurance, that pipefs superblock still exist. IOW, we can use this method only in pipefs mount-umount notification subscribers callbacks. Signed-off-by: Stanislav Kinsbursky Signed-off-by: Trond Myklebust --- include/linux/sunrpc/rpc_pipe_fs.h | 3 +++ net/sunrpc/rpc_pipe.c | 16 ++++++++++++++++ 2 files changed, 19 insertions(+) (limited to 'include/linux') diff --git a/include/linux/sunrpc/rpc_pipe_fs.h b/include/linux/sunrpc/rpc_pipe_fs.h index d39782ce6c67..2f3382230141 100644 --- a/include/linux/sunrpc/rpc_pipe_fs.h +++ b/include/linux/sunrpc/rpc_pipe_fs.h @@ -51,6 +51,9 @@ enum { RPC_PIPEFS_UMOUNT, }; +extern struct dentry *rpc_d_lookup_sb(const struct super_block *sb, + const unsigned char *dir_name); + extern ssize_t rpc_pipe_generic_upcall(struct file *, struct rpc_pipe_msg *, char __user *, size_t); extern int rpc_queue_upcall(struct inode *, struct rpc_pipe_msg *); diff --git a/net/sunrpc/rpc_pipe.c b/net/sunrpc/rpc_pipe.c index 58a5062df260..6f295e6c12a0 100644 --- a/net/sunrpc/rpc_pipe.c +++ b/net/sunrpc/rpc_pipe.c @@ -1007,6 +1007,22 @@ static const struct rpc_filelist files[] = { }, }; +/* + * This call can be used only in RPC pipefs mount notification hooks. + */ +struct dentry *rpc_d_lookup_sb(const struct super_block *sb, + const unsigned char *dir_name) +{ + struct qstr dir = { + .name = dir_name, + .len = strlen(dir_name), + .hash = full_name_hash(dir_name, strlen(dir_name)), + }; + + return d_lookup(sb->s_root, &dir); +} +EXPORT_SYMBOL_GPL(rpc_d_lookup_sb); + static int rpc_fill_super(struct super_block *sb, void *data, int silent) { -- cgit v1.2.3 From c21a588f35b1c50304e505fad542b3aab0814266 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Mon, 26 Dec 2011 15:39:39 +0300 Subject: SUNRPC: pipefs per-net operations helper introduced During per-net pipes creation and destruction we have to make sure, that pipefs sb exists for the whole creation/destruction cycle. This is done by using special mutex which controls pipefs sb reference on network namespace context. Helper consists of two parts: first of them (rpc_get_dentry_net) searches for dentry with specified name and returns with mutex taken on success. When pipe creation or destructions is completed, caller should release this mutex by rpc_put_dentry_net call. Signed-off-by: Stanislav Kinsbursky Signed-off-by: Trond Myklebust --- include/linux/sunrpc/rpc_pipe_fs.h | 3 +++ net/sunrpc/netns.h | 1 + net/sunrpc/rpc_pipe.c | 36 ++++++++++++++++++++++++++++++++++++ net/sunrpc/sunrpc_syms.c | 1 + 4 files changed, 41 insertions(+) (limited to 'include/linux') diff --git a/include/linux/sunrpc/rpc_pipe_fs.h b/include/linux/sunrpc/rpc_pipe_fs.h index 2f3382230141..c13fca34dc9c 100644 --- a/include/linux/sunrpc/rpc_pipe_fs.h +++ b/include/linux/sunrpc/rpc_pipe_fs.h @@ -53,6 +53,9 @@ enum { extern struct dentry *rpc_d_lookup_sb(const struct super_block *sb, const unsigned char *dir_name); +extern void rpc_pipefs_init_net(struct net *net); +extern struct super_block *rpc_get_sb_net(const struct net *net); +extern void rpc_put_sb_net(const struct net *net); extern ssize_t rpc_pipe_generic_upcall(struct file *, struct rpc_pipe_msg *, char __user *, size_t); diff --git a/net/sunrpc/netns.h b/net/sunrpc/netns.h index b3842529aec9..11d2f4863403 100644 --- a/net/sunrpc/netns.h +++ b/net/sunrpc/netns.h @@ -11,6 +11,7 @@ struct sunrpc_net { struct cache_detail *ip_map_cache; struct super_block *pipefs_sb; + struct mutex pipefs_sb_lock; }; extern int sunrpc_net_id; diff --git a/net/sunrpc/rpc_pipe.c b/net/sunrpc/rpc_pipe.c index e5e1f357b561..f075f8817773 100644 --- a/net/sunrpc/rpc_pipe.c +++ b/net/sunrpc/rpc_pipe.c @@ -1023,6 +1023,40 @@ struct dentry *rpc_d_lookup_sb(const struct super_block *sb, } EXPORT_SYMBOL_GPL(rpc_d_lookup_sb); +void rpc_pipefs_init_net(struct net *net) +{ + struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); + + mutex_init(&sn->pipefs_sb_lock); +} + +/* + * This call will be used for per network namespace operations calls. + * Note: Function will be returned with pipefs_sb_lock taken if superblock was + * found. This lock have to be released by rpc_put_sb_net() when all operations + * will be completed. + */ +struct super_block *rpc_get_sb_net(const struct net *net) +{ + struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); + + mutex_lock(&sn->pipefs_sb_lock); + if (sn->pipefs_sb) + return sn->pipefs_sb; + mutex_unlock(&sn->pipefs_sb_lock); + return NULL; +} +EXPORT_SYMBOL_GPL(rpc_get_sb_net); + +void rpc_put_sb_net(const struct net *net) +{ + struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); + + BUG_ON(sn->pipefs_sb == NULL); + mutex_unlock(&sn->pipefs_sb_lock); +} +EXPORT_SYMBOL_GPL(rpc_put_sb_net); + static int rpc_fill_super(struct super_block *sb, void *data, int silent) { @@ -1077,7 +1111,9 @@ void rpc_kill_sb(struct super_block *sb) struct net *net = sb->s_fs_info; struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); + mutex_lock(&sn->pipefs_sb_lock); sn->pipefs_sb = NULL; + mutex_unlock(&sn->pipefs_sb_lock); put_net(net); blocking_notifier_call_chain(&rpc_pipefs_notifier_list, RPC_PIPEFS_UMOUNT, diff --git a/net/sunrpc/sunrpc_syms.c b/net/sunrpc/sunrpc_syms.c index 8ec9778c3f4a..7086d11589c8 100644 --- a/net/sunrpc/sunrpc_syms.c +++ b/net/sunrpc/sunrpc_syms.c @@ -38,6 +38,7 @@ static __net_init int sunrpc_init_net(struct net *net) if (err) goto err_ipmap; + rpc_pipefs_init_net(net); return 0; err_ipmap: -- cgit v1.2.3 From 766347bec3490111e1c4482af7c7394868c2aed1 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Mon, 26 Dec 2011 15:43:23 +0300 Subject: SUNRPC: replace inode lock with pipe lock for RPC PipeFS operations Currenly, inode i_lock is used to provide concurrent access to SUNPRC PipeFS pipes. It looks redundant, since now other use of inode is present in most of these places and thus can be easely replaced, which will allow to remove most of inode references from PipeFS code. This is a first step towards to removing PipeFS inode references from kernel code other than PipeFS itself. Signed-off-by: Stanislav Kinsbursky Signed-off-by: Trond Myklebust --- include/linux/sunrpc/rpc_pipe_fs.h | 1 + net/sunrpc/auth_gss/auth_gss.c | 57 +++++++++++++++++++------------------- net/sunrpc/rpc_pipe.c | 38 ++++++++++++------------- 3 files changed, 48 insertions(+), 48 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sunrpc/rpc_pipe_fs.h b/include/linux/sunrpc/rpc_pipe_fs.h index c13fca34dc9c..c93ea8689bc8 100644 --- a/include/linux/sunrpc/rpc_pipe_fs.h +++ b/include/linux/sunrpc/rpc_pipe_fs.h @@ -35,6 +35,7 @@ struct rpc_inode { int flags; struct delayed_work queue_timeout; const struct rpc_pipe_ops *ops; + spinlock_t lock; }; static inline struct rpc_inode * diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index affa631ac1ab..a0844f92a447 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c @@ -112,7 +112,7 @@ gss_put_ctx(struct gss_cl_ctx *ctx) /* gss_cred_set_ctx: * called by gss_upcall_callback and gss_create_upcall in order * to set the gss context. The actual exchange of an old context - * and a new one is protected by the inode->i_lock. + * and a new one is protected by the rpci->lock. */ static void gss_cred_set_ctx(struct rpc_cred *cred, struct gss_cl_ctx *ctx) @@ -316,17 +316,16 @@ static inline struct gss_upcall_msg * gss_add_msg(struct gss_upcall_msg *gss_msg) { struct rpc_inode *rpci = gss_msg->inode; - struct inode *inode = &rpci->vfs_inode; struct gss_upcall_msg *old; - spin_lock(&inode->i_lock); + spin_lock(&rpci->lock); old = __gss_find_upcall(rpci, gss_msg->uid); if (old == NULL) { atomic_inc(&gss_msg->count); list_add(&gss_msg->list, &rpci->in_downcall); } else gss_msg = old; - spin_unlock(&inode->i_lock); + spin_unlock(&rpci->lock); return gss_msg; } @@ -342,14 +341,14 @@ __gss_unhash_msg(struct gss_upcall_msg *gss_msg) static void gss_unhash_msg(struct gss_upcall_msg *gss_msg) { - struct inode *inode = &gss_msg->inode->vfs_inode; + struct rpc_inode *rpci = gss_msg->inode; if (list_empty(&gss_msg->list)) return; - spin_lock(&inode->i_lock); + spin_lock(&rpci->lock); if (!list_empty(&gss_msg->list)) __gss_unhash_msg(gss_msg); - spin_unlock(&inode->i_lock); + spin_unlock(&rpci->lock); } static void @@ -376,11 +375,11 @@ gss_upcall_callback(struct rpc_task *task) struct gss_cred *gss_cred = container_of(task->tk_rqstp->rq_cred, struct gss_cred, gc_base); struct gss_upcall_msg *gss_msg = gss_cred->gc_upcall; - struct inode *inode = &gss_msg->inode->vfs_inode; + struct rpc_inode *rpci = gss_msg->inode; - spin_lock(&inode->i_lock); + spin_lock(&rpci->lock); gss_handle_downcall_result(gss_cred, gss_msg); - spin_unlock(&inode->i_lock); + spin_unlock(&rpci->lock); task->tk_status = gss_msg->msg.errno; gss_release_msg(gss_msg); } @@ -506,7 +505,7 @@ gss_refresh_upcall(struct rpc_task *task) struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base); struct gss_upcall_msg *gss_msg; - struct inode *inode; + struct rpc_inode *rpci; int err = 0; dprintk("RPC: %5u gss_refresh_upcall for uid %u\n", task->tk_pid, @@ -524,8 +523,8 @@ gss_refresh_upcall(struct rpc_task *task) err = PTR_ERR(gss_msg); goto out; } - inode = &gss_msg->inode->vfs_inode; - spin_lock(&inode->i_lock); + rpci = gss_msg->inode; + spin_lock(&rpci->lock); if (gss_cred->gc_upcall != NULL) rpc_sleep_on(&gss_cred->gc_upcall->rpc_waitqueue, task, NULL); else if (gss_msg->ctx == NULL && gss_msg->msg.errno >= 0) { @@ -538,7 +537,7 @@ gss_refresh_upcall(struct rpc_task *task) gss_handle_downcall_result(gss_cred, gss_msg); err = gss_msg->msg.errno; } - spin_unlock(&inode->i_lock); + spin_unlock(&rpci->lock); gss_release_msg(gss_msg); out: dprintk("RPC: %5u gss_refresh_upcall for uid %u result %d\n", @@ -549,7 +548,7 @@ out: static inline int gss_create_upcall(struct gss_auth *gss_auth, struct gss_cred *gss_cred) { - struct inode *inode; + struct rpc_inode *rpci; struct rpc_cred *cred = &gss_cred->gc_base; struct gss_upcall_msg *gss_msg; DEFINE_WAIT(wait); @@ -573,14 +572,14 @@ retry: err = PTR_ERR(gss_msg); goto out; } - inode = &gss_msg->inode->vfs_inode; + rpci = gss_msg->inode; for (;;) { prepare_to_wait(&gss_msg->waitqueue, &wait, TASK_KILLABLE); - spin_lock(&inode->i_lock); + spin_lock(&rpci->lock); if (gss_msg->ctx != NULL || gss_msg->msg.errno < 0) { break; } - spin_unlock(&inode->i_lock); + spin_unlock(&rpci->lock); if (fatal_signal_pending(current)) { err = -ERESTARTSYS; goto out_intr; @@ -591,7 +590,7 @@ retry: gss_cred_set_ctx(cred, gss_msg->ctx); else err = gss_msg->msg.errno; - spin_unlock(&inode->i_lock); + spin_unlock(&rpci->lock); out_intr: finish_wait(&gss_msg->waitqueue, &wait); gss_release_msg(gss_msg); @@ -609,7 +608,7 @@ gss_pipe_downcall(struct file *filp, const char __user *src, size_t mlen) const void *p, *end; void *buf; struct gss_upcall_msg *gss_msg; - struct inode *inode = filp->f_path.dentry->d_inode; + struct rpc_inode *rpci = RPC_I(filp->f_dentry->d_inode); struct gss_cl_ctx *ctx; uid_t uid; ssize_t err = -EFBIG; @@ -639,14 +638,14 @@ gss_pipe_downcall(struct file *filp, const char __user *src, size_t mlen) err = -ENOENT; /* Find a matching upcall */ - spin_lock(&inode->i_lock); - gss_msg = __gss_find_upcall(RPC_I(inode), uid); + spin_lock(&rpci->lock); + gss_msg = __gss_find_upcall(rpci, uid); if (gss_msg == NULL) { - spin_unlock(&inode->i_lock); + spin_unlock(&rpci->lock); goto err_put_ctx; } list_del_init(&gss_msg->list); - spin_unlock(&inode->i_lock); + spin_unlock(&rpci->lock); p = gss_fill_context(p, end, ctx, gss_msg->auth->mech); if (IS_ERR(p)) { @@ -674,9 +673,9 @@ gss_pipe_downcall(struct file *filp, const char __user *src, size_t mlen) err = mlen; err_release_msg: - spin_lock(&inode->i_lock); + spin_lock(&rpci->lock); __gss_unhash_msg(gss_msg); - spin_unlock(&inode->i_lock); + spin_unlock(&rpci->lock); gss_release_msg(gss_msg); err_put_ctx: gss_put_ctx(ctx); @@ -726,7 +725,7 @@ gss_pipe_release(struct inode *inode) struct gss_upcall_msg *gss_msg; restart: - spin_lock(&inode->i_lock); + spin_lock(&rpci->lock); list_for_each_entry(gss_msg, &rpci->in_downcall, list) { if (!list_empty(&gss_msg->msg.list)) @@ -734,11 +733,11 @@ restart: gss_msg->msg.errno = -EPIPE; atomic_inc(&gss_msg->count); __gss_unhash_msg(gss_msg); - spin_unlock(&inode->i_lock); + spin_unlock(&rpci->lock); gss_release_msg(gss_msg); goto restart; } - spin_unlock(&inode->i_lock); + spin_unlock(&rpci->lock); put_pipe_version(); } diff --git a/net/sunrpc/rpc_pipe.c b/net/sunrpc/rpc_pipe.c index 9a881138fead..16d9b9a701a4 100644 --- a/net/sunrpc/rpc_pipe.c +++ b/net/sunrpc/rpc_pipe.c @@ -83,12 +83,11 @@ rpc_timeout_upcall_queue(struct work_struct *work) LIST_HEAD(free_list); struct rpc_inode *rpci = container_of(work, struct rpc_inode, queue_timeout.work); - struct inode *inode = &rpci->vfs_inode; void (*destroy_msg)(struct rpc_pipe_msg *); - spin_lock(&inode->i_lock); + spin_lock(&rpci->lock); if (rpci->ops == NULL) { - spin_unlock(&inode->i_lock); + spin_unlock(&rpci->lock); return; } destroy_msg = rpci->ops->destroy_msg; @@ -96,7 +95,7 @@ rpc_timeout_upcall_queue(struct work_struct *work) list_splice_init(&rpci->pipe, &free_list); rpci->pipelen = 0; } - spin_unlock(&inode->i_lock); + spin_unlock(&rpci->lock); rpc_purge_list(rpci, &free_list, destroy_msg, -ETIMEDOUT); } @@ -136,7 +135,7 @@ rpc_queue_upcall(struct inode *inode, struct rpc_pipe_msg *msg) struct rpc_inode *rpci = RPC_I(inode); int res = -EPIPE; - spin_lock(&inode->i_lock); + spin_lock(&rpci->lock); if (rpci->ops == NULL) goto out; if (rpci->nreaders) { @@ -153,7 +152,7 @@ rpc_queue_upcall(struct inode *inode, struct rpc_pipe_msg *msg) res = 0; } out: - spin_unlock(&inode->i_lock); + spin_unlock(&rpci->lock); wake_up(&rpci->waitq); return res; } @@ -176,14 +175,14 @@ rpc_close_pipes(struct inode *inode) ops = rpci->ops; if (ops != NULL) { LIST_HEAD(free_list); - spin_lock(&inode->i_lock); + spin_lock(&rpci->lock); need_release = rpci->nreaders != 0 || rpci->nwriters != 0; rpci->nreaders = 0; list_splice_init(&rpci->in_upcall, &free_list); list_splice_init(&rpci->pipe, &free_list); rpci->pipelen = 0; rpci->ops = NULL; - spin_unlock(&inode->i_lock); + spin_unlock(&rpci->lock); rpc_purge_list(rpci, &free_list, ops->destroy_msg, -EPIPE); rpci->nwriters = 0; if (need_release && ops->release_pipe) @@ -255,10 +254,10 @@ rpc_pipe_release(struct inode *inode, struct file *filp) goto out; msg = filp->private_data; if (msg != NULL) { - spin_lock(&inode->i_lock); + spin_lock(&rpci->lock); msg->errno = -EAGAIN; list_del_init(&msg->list); - spin_unlock(&inode->i_lock); + spin_unlock(&rpci->lock); rpci->ops->destroy_msg(msg); } if (filp->f_mode & FMODE_WRITE) @@ -267,10 +266,10 @@ rpc_pipe_release(struct inode *inode, struct file *filp) rpci->nreaders --; if (rpci->nreaders == 0) { LIST_HEAD(free_list); - spin_lock(&inode->i_lock); + spin_lock(&rpci->lock); list_splice_init(&rpci->pipe, &free_list); rpci->pipelen = 0; - spin_unlock(&inode->i_lock); + spin_unlock(&rpci->lock); rpc_purge_list(rpci, &free_list, rpci->ops->destroy_msg, -EAGAIN); } @@ -298,7 +297,7 @@ rpc_pipe_read(struct file *filp, char __user *buf, size_t len, loff_t *offset) } msg = filp->private_data; if (msg == NULL) { - spin_lock(&inode->i_lock); + spin_lock(&rpci->lock); if (!list_empty(&rpci->pipe)) { msg = list_entry(rpci->pipe.next, struct rpc_pipe_msg, @@ -308,7 +307,7 @@ rpc_pipe_read(struct file *filp, char __user *buf, size_t len, loff_t *offset) filp->private_data = msg; msg->copied = 0; } - spin_unlock(&inode->i_lock); + spin_unlock(&rpci->lock); if (msg == NULL) goto out_unlock; } @@ -316,9 +315,9 @@ rpc_pipe_read(struct file *filp, char __user *buf, size_t len, loff_t *offset) res = rpci->ops->upcall(filp, msg, buf, len); if (res < 0 || msg->len == msg->copied) { filp->private_data = NULL; - spin_lock(&inode->i_lock); + spin_lock(&rpci->lock); list_del_init(&msg->list); - spin_unlock(&inode->i_lock); + spin_unlock(&rpci->lock); rpci->ops->destroy_msg(msg); } out_unlock: @@ -367,9 +366,9 @@ rpc_pipe_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) switch (cmd) { case FIONREAD: - spin_lock(&inode->i_lock); + spin_lock(&rpci->lock); if (rpci->ops == NULL) { - spin_unlock(&inode->i_lock); + spin_unlock(&rpci->lock); return -EPIPE; } len = rpci->pipelen; @@ -378,7 +377,7 @@ rpc_pipe_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) msg = filp->private_data; len += msg->len - msg->copied; } - spin_unlock(&inode->i_lock); + spin_unlock(&rpci->lock); return put_user(len, (int __user *)arg); default: return -EINVAL; @@ -1153,6 +1152,7 @@ init_once(void *foo) INIT_DELAYED_WORK(&rpci->queue_timeout, rpc_timeout_upcall_queue); rpci->ops = NULL; + spin_lock_init(&rpci->lock); } int register_rpc_pipefs(void) -- cgit v1.2.3 From ba9e097593f371ebd102580a0c5b1b2cf55636a0 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Mon, 26 Dec 2011 15:43:32 +0300 Subject: SUNRPC: split SUNPRC PipeFS pipe data and inode creation Generally, pipe data is used only for pipes, and thus allocating space for it on every RPC inode allocation is redundant. This patch splits private SUNRPC PipeFS pipe data and inode, makes pipe data allocated only for pipe inodes. This patch is also is a next step towards to to removing PipeFS inode references from kernel code other than PipeFS itself. Signed-off-by: Stanislav Kinsbursky Signed-off-by: Trond Myklebust --- include/linux/sunrpc/rpc_pipe_fs.h | 10 +- net/sunrpc/auth_gss/auth_gss.c | 46 ++++---- net/sunrpc/rpc_pipe.c | 208 ++++++++++++++++++++----------------- 3 files changed, 142 insertions(+), 122 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sunrpc/rpc_pipe_fs.h b/include/linux/sunrpc/rpc_pipe_fs.h index c93ea8689bc8..57512c23d34c 100644 --- a/include/linux/sunrpc/rpc_pipe_fs.h +++ b/include/linux/sunrpc/rpc_pipe_fs.h @@ -21,9 +21,7 @@ struct rpc_pipe_ops { void (*destroy_msg)(struct rpc_pipe_msg *); }; -struct rpc_inode { - struct inode vfs_inode; - void *private; +struct rpc_pipe { struct list_head pipe; struct list_head in_upcall; struct list_head in_downcall; @@ -38,6 +36,12 @@ struct rpc_inode { spinlock_t lock; }; +struct rpc_inode { + struct inode vfs_inode; + void *private; + struct rpc_pipe *pipe; +}; + static inline struct rpc_inode * RPC_I(struct inode *inode) { diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index a0844f92a447..e933484e55ef 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c @@ -112,7 +112,7 @@ gss_put_ctx(struct gss_cl_ctx *ctx) /* gss_cred_set_ctx: * called by gss_upcall_callback and gss_create_upcall in order * to set the gss context. The actual exchange of an old context - * and a new one is protected by the rpci->lock. + * and a new one is protected by the rpci->pipe->lock. */ static void gss_cred_set_ctx(struct rpc_cred *cred, struct gss_cl_ctx *ctx) @@ -297,7 +297,7 @@ static struct gss_upcall_msg * __gss_find_upcall(struct rpc_inode *rpci, uid_t uid) { struct gss_upcall_msg *pos; - list_for_each_entry(pos, &rpci->in_downcall, list) { + list_for_each_entry(pos, &rpci->pipe->in_downcall, list) { if (pos->uid != uid) continue; atomic_inc(&pos->count); @@ -318,14 +318,14 @@ gss_add_msg(struct gss_upcall_msg *gss_msg) struct rpc_inode *rpci = gss_msg->inode; struct gss_upcall_msg *old; - spin_lock(&rpci->lock); + spin_lock(&rpci->pipe->lock); old = __gss_find_upcall(rpci, gss_msg->uid); if (old == NULL) { atomic_inc(&gss_msg->count); - list_add(&gss_msg->list, &rpci->in_downcall); + list_add(&gss_msg->list, &rpci->pipe->in_downcall); } else gss_msg = old; - spin_unlock(&rpci->lock); + spin_unlock(&rpci->pipe->lock); return gss_msg; } @@ -345,10 +345,10 @@ gss_unhash_msg(struct gss_upcall_msg *gss_msg) if (list_empty(&gss_msg->list)) return; - spin_lock(&rpci->lock); + spin_lock(&rpci->pipe->lock); if (!list_empty(&gss_msg->list)) __gss_unhash_msg(gss_msg); - spin_unlock(&rpci->lock); + spin_unlock(&rpci->pipe->lock); } static void @@ -377,9 +377,9 @@ gss_upcall_callback(struct rpc_task *task) struct gss_upcall_msg *gss_msg = gss_cred->gc_upcall; struct rpc_inode *rpci = gss_msg->inode; - spin_lock(&rpci->lock); + spin_lock(&rpci->pipe->lock); gss_handle_downcall_result(gss_cred, gss_msg); - spin_unlock(&rpci->lock); + spin_unlock(&rpci->pipe->lock); task->tk_status = gss_msg->msg.errno; gss_release_msg(gss_msg); } @@ -524,7 +524,7 @@ gss_refresh_upcall(struct rpc_task *task) goto out; } rpci = gss_msg->inode; - spin_lock(&rpci->lock); + spin_lock(&rpci->pipe->lock); if (gss_cred->gc_upcall != NULL) rpc_sleep_on(&gss_cred->gc_upcall->rpc_waitqueue, task, NULL); else if (gss_msg->ctx == NULL && gss_msg->msg.errno >= 0) { @@ -537,7 +537,7 @@ gss_refresh_upcall(struct rpc_task *task) gss_handle_downcall_result(gss_cred, gss_msg); err = gss_msg->msg.errno; } - spin_unlock(&rpci->lock); + spin_unlock(&rpci->pipe->lock); gss_release_msg(gss_msg); out: dprintk("RPC: %5u gss_refresh_upcall for uid %u result %d\n", @@ -575,11 +575,11 @@ retry: rpci = gss_msg->inode; for (;;) { prepare_to_wait(&gss_msg->waitqueue, &wait, TASK_KILLABLE); - spin_lock(&rpci->lock); + spin_lock(&rpci->pipe->lock); if (gss_msg->ctx != NULL || gss_msg->msg.errno < 0) { break; } - spin_unlock(&rpci->lock); + spin_unlock(&rpci->pipe->lock); if (fatal_signal_pending(current)) { err = -ERESTARTSYS; goto out_intr; @@ -590,7 +590,7 @@ retry: gss_cred_set_ctx(cred, gss_msg->ctx); else err = gss_msg->msg.errno; - spin_unlock(&rpci->lock); + spin_unlock(&rpci->pipe->lock); out_intr: finish_wait(&gss_msg->waitqueue, &wait); gss_release_msg(gss_msg); @@ -638,14 +638,14 @@ gss_pipe_downcall(struct file *filp, const char __user *src, size_t mlen) err = -ENOENT; /* Find a matching upcall */ - spin_lock(&rpci->lock); + spin_lock(&rpci->pipe->lock); gss_msg = __gss_find_upcall(rpci, uid); if (gss_msg == NULL) { - spin_unlock(&rpci->lock); + spin_unlock(&rpci->pipe->lock); goto err_put_ctx; } list_del_init(&gss_msg->list); - spin_unlock(&rpci->lock); + spin_unlock(&rpci->pipe->lock); p = gss_fill_context(p, end, ctx, gss_msg->auth->mech); if (IS_ERR(p)) { @@ -673,9 +673,9 @@ gss_pipe_downcall(struct file *filp, const char __user *src, size_t mlen) err = mlen; err_release_msg: - spin_lock(&rpci->lock); + spin_lock(&rpci->pipe->lock); __gss_unhash_msg(gss_msg); - spin_unlock(&rpci->lock); + spin_unlock(&rpci->pipe->lock); gss_release_msg(gss_msg); err_put_ctx: gss_put_ctx(ctx); @@ -725,19 +725,19 @@ gss_pipe_release(struct inode *inode) struct gss_upcall_msg *gss_msg; restart: - spin_lock(&rpci->lock); - list_for_each_entry(gss_msg, &rpci->in_downcall, list) { + spin_lock(&rpci->pipe->lock); + list_for_each_entry(gss_msg, &rpci->pipe->in_downcall, list) { if (!list_empty(&gss_msg->msg.list)) continue; gss_msg->msg.errno = -EPIPE; atomic_inc(&gss_msg->count); __gss_unhash_msg(gss_msg); - spin_unlock(&rpci->lock); + spin_unlock(&rpci->pipe->lock); gss_release_msg(gss_msg); goto restart; } - spin_unlock(&rpci->lock); + spin_unlock(&rpci->pipe->lock); put_pipe_version(); } diff --git a/net/sunrpc/rpc_pipe.c b/net/sunrpc/rpc_pipe.c index 16d9b9a701a4..b6f6555128db 100644 --- a/net/sunrpc/rpc_pipe.c +++ b/net/sunrpc/rpc_pipe.c @@ -61,7 +61,7 @@ void rpc_pipefs_notifier_unregister(struct notifier_block *nb) } EXPORT_SYMBOL_GPL(rpc_pipefs_notifier_unregister); -static void rpc_purge_list(struct rpc_inode *rpci, struct list_head *head, +static void rpc_purge_list(struct rpc_pipe *pipe, struct list_head *head, void (*destroy_msg)(struct rpc_pipe_msg *), int err) { struct rpc_pipe_msg *msg; @@ -74,29 +74,29 @@ static void rpc_purge_list(struct rpc_inode *rpci, struct list_head *head, msg->errno = err; destroy_msg(msg); } while (!list_empty(head)); - wake_up(&rpci->waitq); + wake_up(&pipe->waitq); } static void rpc_timeout_upcall_queue(struct work_struct *work) { LIST_HEAD(free_list); - struct rpc_inode *rpci = - container_of(work, struct rpc_inode, queue_timeout.work); + struct rpc_pipe *pipe = + container_of(work, struct rpc_pipe, queue_timeout.work); void (*destroy_msg)(struct rpc_pipe_msg *); - spin_lock(&rpci->lock); - if (rpci->ops == NULL) { - spin_unlock(&rpci->lock); + spin_lock(&pipe->lock); + if (pipe->ops == NULL) { + spin_unlock(&pipe->lock); return; } - destroy_msg = rpci->ops->destroy_msg; - if (rpci->nreaders == 0) { - list_splice_init(&rpci->pipe, &free_list); - rpci->pipelen = 0; + destroy_msg = pipe->ops->destroy_msg; + if (pipe->nreaders == 0) { + list_splice_init(&pipe->pipe, &free_list); + pipe->pipelen = 0; } - spin_unlock(&rpci->lock); - rpc_purge_list(rpci, &free_list, destroy_msg, -ETIMEDOUT); + spin_unlock(&pipe->lock); + rpc_purge_list(pipe, &free_list, destroy_msg, -ETIMEDOUT); } ssize_t rpc_pipe_generic_upcall(struct file *filp, struct rpc_pipe_msg *msg, @@ -135,25 +135,25 @@ rpc_queue_upcall(struct inode *inode, struct rpc_pipe_msg *msg) struct rpc_inode *rpci = RPC_I(inode); int res = -EPIPE; - spin_lock(&rpci->lock); - if (rpci->ops == NULL) + spin_lock(&rpci->pipe->lock); + if (rpci->pipe->ops == NULL) goto out; - if (rpci->nreaders) { - list_add_tail(&msg->list, &rpci->pipe); - rpci->pipelen += msg->len; + if (rpci->pipe->nreaders) { + list_add_tail(&msg->list, &rpci->pipe->pipe); + rpci->pipe->pipelen += msg->len; res = 0; - } else if (rpci->flags & RPC_PIPE_WAIT_FOR_OPEN) { - if (list_empty(&rpci->pipe)) + } else if (rpci->pipe->flags & RPC_PIPE_WAIT_FOR_OPEN) { + if (list_empty(&rpci->pipe->pipe)) queue_delayed_work(rpciod_workqueue, - &rpci->queue_timeout, + &rpci->pipe->queue_timeout, RPC_UPCALL_TIMEOUT); - list_add_tail(&msg->list, &rpci->pipe); - rpci->pipelen += msg->len; + list_add_tail(&msg->list, &rpci->pipe->pipe); + rpci->pipe->pipelen += msg->len; res = 0; } out: - spin_unlock(&rpci->lock); - wake_up(&rpci->waitq); + spin_unlock(&rpci->pipe->lock); + wake_up(&rpci->pipe->waitq); return res; } EXPORT_SYMBOL_GPL(rpc_queue_upcall); @@ -167,27 +167,27 @@ rpc_inode_setowner(struct inode *inode, void *private) static void rpc_close_pipes(struct inode *inode) { - struct rpc_inode *rpci = RPC_I(inode); + struct rpc_pipe *pipe = RPC_I(inode)->pipe; const struct rpc_pipe_ops *ops; int need_release; mutex_lock(&inode->i_mutex); - ops = rpci->ops; + ops = pipe->ops; if (ops != NULL) { LIST_HEAD(free_list); - spin_lock(&rpci->lock); - need_release = rpci->nreaders != 0 || rpci->nwriters != 0; - rpci->nreaders = 0; - list_splice_init(&rpci->in_upcall, &free_list); - list_splice_init(&rpci->pipe, &free_list); - rpci->pipelen = 0; - rpci->ops = NULL; - spin_unlock(&rpci->lock); - rpc_purge_list(rpci, &free_list, ops->destroy_msg, -EPIPE); - rpci->nwriters = 0; + spin_lock(&pipe->lock); + need_release = pipe->nreaders != 0 || pipe->nwriters != 0; + pipe->nreaders = 0; + list_splice_init(&pipe->in_upcall, &free_list); + list_splice_init(&pipe->pipe, &free_list); + pipe->pipelen = 0; + pipe->ops = NULL; + spin_unlock(&pipe->lock); + rpc_purge_list(pipe, &free_list, ops->destroy_msg, -EPIPE); + pipe->nwriters = 0; if (need_release && ops->release_pipe) ops->release_pipe(inode); - cancel_delayed_work_sync(&rpci->queue_timeout); + cancel_delayed_work_sync(&pipe->queue_timeout); } rpc_inode_setowner(inode, NULL); mutex_unlock(&inode->i_mutex); @@ -207,6 +207,7 @@ static void rpc_i_callback(struct rcu_head *head) { struct inode *inode = container_of(head, struct inode, i_rcu); + kfree(RPC_I(inode)->pipe); kmem_cache_free(rpc_inode_cachep, RPC_I(inode)); } @@ -224,18 +225,18 @@ rpc_pipe_open(struct inode *inode, struct file *filp) int res = -ENXIO; mutex_lock(&inode->i_mutex); - if (rpci->ops == NULL) + if (rpci->pipe->ops == NULL) goto out; - first_open = rpci->nreaders == 0 && rpci->nwriters == 0; - if (first_open && rpci->ops->open_pipe) { - res = rpci->ops->open_pipe(inode); + first_open = rpci->pipe->nreaders == 0 && rpci->pipe->nwriters == 0; + if (first_open && rpci->pipe->ops->open_pipe) { + res = rpci->pipe->ops->open_pipe(inode); if (res) goto out; } if (filp->f_mode & FMODE_READ) - rpci->nreaders++; + rpci->pipe->nreaders++; if (filp->f_mode & FMODE_WRITE) - rpci->nwriters++; + rpci->pipe->nwriters++; res = 0; out: mutex_unlock(&inode->i_mutex); @@ -245,38 +246,38 @@ out: static int rpc_pipe_release(struct inode *inode, struct file *filp) { - struct rpc_inode *rpci = RPC_I(inode); + struct rpc_pipe *pipe = RPC_I(inode)->pipe; struct rpc_pipe_msg *msg; int last_close; mutex_lock(&inode->i_mutex); - if (rpci->ops == NULL) + if (pipe->ops == NULL) goto out; msg = filp->private_data; if (msg != NULL) { - spin_lock(&rpci->lock); + spin_lock(&pipe->lock); msg->errno = -EAGAIN; list_del_init(&msg->list); - spin_unlock(&rpci->lock); - rpci->ops->destroy_msg(msg); + spin_unlock(&pipe->lock); + pipe->ops->destroy_msg(msg); } if (filp->f_mode & FMODE_WRITE) - rpci->nwriters --; + pipe->nwriters --; if (filp->f_mode & FMODE_READ) { - rpci->nreaders --; - if (rpci->nreaders == 0) { + pipe->nreaders --; + if (pipe->nreaders == 0) { LIST_HEAD(free_list); - spin_lock(&rpci->lock); - list_splice_init(&rpci->pipe, &free_list); - rpci->pipelen = 0; - spin_unlock(&rpci->lock); - rpc_purge_list(rpci, &free_list, - rpci->ops->destroy_msg, -EAGAIN); + spin_lock(&pipe->lock); + list_splice_init(&pipe->pipe, &free_list); + pipe->pipelen = 0; + spin_unlock(&pipe->lock); + rpc_purge_list(pipe, &free_list, + pipe->ops->destroy_msg, -EAGAIN); } } - last_close = rpci->nwriters == 0 && rpci->nreaders == 0; - if (last_close && rpci->ops->release_pipe) - rpci->ops->release_pipe(inode); + last_close = pipe->nwriters == 0 && pipe->nreaders == 0; + if (last_close && pipe->ops->release_pipe) + pipe->ops->release_pipe(inode); out: mutex_unlock(&inode->i_mutex); return 0; @@ -291,34 +292,34 @@ rpc_pipe_read(struct file *filp, char __user *buf, size_t len, loff_t *offset) int res = 0; mutex_lock(&inode->i_mutex); - if (rpci->ops == NULL) { + if (rpci->pipe->ops == NULL) { res = -EPIPE; goto out_unlock; } msg = filp->private_data; if (msg == NULL) { - spin_lock(&rpci->lock); - if (!list_empty(&rpci->pipe)) { - msg = list_entry(rpci->pipe.next, + spin_lock(&rpci->pipe->lock); + if (!list_empty(&rpci->pipe->pipe)) { + msg = list_entry(rpci->pipe->pipe.next, struct rpc_pipe_msg, list); - list_move(&msg->list, &rpci->in_upcall); - rpci->pipelen -= msg->len; + list_move(&msg->list, &rpci->pipe->in_upcall); + rpci->pipe->pipelen -= msg->len; filp->private_data = msg; msg->copied = 0; } - spin_unlock(&rpci->lock); + spin_unlock(&rpci->pipe->lock); if (msg == NULL) goto out_unlock; } /* NOTE: it is up to the callback to update msg->copied */ - res = rpci->ops->upcall(filp, msg, buf, len); + res = rpci->pipe->ops->upcall(filp, msg, buf, len); if (res < 0 || msg->len == msg->copied) { filp->private_data = NULL; - spin_lock(&rpci->lock); + spin_lock(&rpci->pipe->lock); list_del_init(&msg->list); - spin_unlock(&rpci->lock); - rpci->ops->destroy_msg(msg); + spin_unlock(&rpci->pipe->lock); + rpci->pipe->ops->destroy_msg(msg); } out_unlock: mutex_unlock(&inode->i_mutex); @@ -334,8 +335,8 @@ rpc_pipe_write(struct file *filp, const char __user *buf, size_t len, loff_t *of mutex_lock(&inode->i_mutex); res = -EPIPE; - if (rpci->ops != NULL) - res = rpci->ops->downcall(filp, buf, len); + if (rpci->pipe->ops != NULL) + res = rpci->pipe->ops->downcall(filp, buf, len); mutex_unlock(&inode->i_mutex); return res; } @@ -347,12 +348,12 @@ rpc_pipe_poll(struct file *filp, struct poll_table_struct *wait) unsigned int mask = 0; rpci = RPC_I(filp->f_path.dentry->d_inode); - poll_wait(filp, &rpci->waitq, wait); + poll_wait(filp, &rpci->pipe->waitq, wait); mask = POLLOUT | POLLWRNORM; - if (rpci->ops == NULL) + if (rpci->pipe->ops == NULL) mask |= POLLERR | POLLHUP; - if (filp->private_data || !list_empty(&rpci->pipe)) + if (filp->private_data || !list_empty(&rpci->pipe->pipe)) mask |= POLLIN | POLLRDNORM; return mask; } @@ -366,18 +367,18 @@ rpc_pipe_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) switch (cmd) { case FIONREAD: - spin_lock(&rpci->lock); - if (rpci->ops == NULL) { - spin_unlock(&rpci->lock); + spin_lock(&rpci->pipe->lock); + if (rpci->pipe->ops == NULL) { + spin_unlock(&rpci->pipe->lock); return -EPIPE; } - len = rpci->pipelen; + len = rpci->pipe->pipelen; if (filp->private_data) { struct rpc_pipe_msg *msg; msg = filp->private_data; len += msg->len - msg->copied; } - spin_unlock(&rpci->lock); + spin_unlock(&rpci->pipe->lock); return put_user(len, (int __user *)arg); default: return -EINVAL; @@ -562,6 +563,23 @@ static int __rpc_mkdir(struct inode *dir, struct dentry *dentry, return 0; } +static void +init_pipe(struct rpc_pipe *pipe) +{ + pipe->nreaders = 0; + pipe->nwriters = 0; + INIT_LIST_HEAD(&pipe->in_upcall); + INIT_LIST_HEAD(&pipe->in_downcall); + INIT_LIST_HEAD(&pipe->pipe); + pipe->pipelen = 0; + init_waitqueue_head(&pipe->waitq); + INIT_DELAYED_WORK(&pipe->queue_timeout, + rpc_timeout_upcall_queue); + pipe->ops = NULL; + spin_lock_init(&pipe->lock); + +} + static int __rpc_mkpipe(struct inode *dir, struct dentry *dentry, umode_t mode, const struct file_operations *i_fop, @@ -569,16 +587,24 @@ static int __rpc_mkpipe(struct inode *dir, struct dentry *dentry, const struct rpc_pipe_ops *ops, int flags) { + struct rpc_pipe *pipe; struct rpc_inode *rpci; int err; + pipe = kzalloc(sizeof(struct rpc_pipe), GFP_KERNEL); + if (!pipe) + return -ENOMEM; + init_pipe(pipe); err = __rpc_create_common(dir, dentry, S_IFIFO | mode, i_fop, private); - if (err) + if (err) { + kfree(pipe); return err; + } rpci = RPC_I(dentry->d_inode); rpci->private = private; - rpci->flags = flags; - rpci->ops = ops; + rpci->pipe = pipe; + rpci->pipe->flags = flags; + rpci->pipe->ops = ops; fsnotify_create(dir, dentry); return 0; } @@ -1142,17 +1168,7 @@ init_once(void *foo) inode_init_once(&rpci->vfs_inode); rpci->private = NULL; - rpci->nreaders = 0; - rpci->nwriters = 0; - INIT_LIST_HEAD(&rpci->in_upcall); - INIT_LIST_HEAD(&rpci->in_downcall); - INIT_LIST_HEAD(&rpci->pipe); - rpci->pipelen = 0; - init_waitqueue_head(&rpci->waitq); - INIT_DELAYED_WORK(&rpci->queue_timeout, - rpc_timeout_upcall_queue); - rpci->ops = NULL; - spin_lock_init(&rpci->lock); + rpci->pipe = NULL; } int register_rpc_pipefs(void) -- cgit v1.2.3 From d706ed1f50d3f7fae61a177183562179abe8e4bb Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Mon, 26 Dec 2011 15:43:49 +0300 Subject: SUNPRC: cleanup RPC PipeFS pipes upcall interface RPC pipe upcall doesn't requires only private pipe data. Thus RPC inode references in this code can be removed. Signed-off-by: Stanislav Kinsbursky Signed-off-by: Trond Myklebust --- fs/nfs/blocklayout/blocklayoutdev.c | 2 +- fs/nfs/blocklayout/blocklayoutdm.c | 2 +- fs/nfs/idmap.c | 4 ++-- include/linux/sunrpc/rpc_pipe_fs.h | 2 +- net/sunrpc/auth_gss/auth_gss.c | 3 +-- net/sunrpc/rpc_pipe.c | 3 +-- 6 files changed, 7 insertions(+), 9 deletions(-) (limited to 'include/linux') diff --git a/fs/nfs/blocklayout/blocklayoutdev.c b/fs/nfs/blocklayout/blocklayoutdev.c index d08ba9107fde..81019190e46d 100644 --- a/fs/nfs/blocklayout/blocklayoutdev.c +++ b/fs/nfs/blocklayout/blocklayoutdev.c @@ -146,7 +146,7 @@ nfs4_blk_decode_device(struct nfs_server *server, dprintk("%s CALLING USERSPACE DAEMON\n", __func__); add_wait_queue(&bl_wq, &wq); - rc = rpc_queue_upcall(bl_device_pipe->d_inode, &msg); + rc = rpc_queue_upcall(RPC_I(bl_device_pipe->d_inode)->pipe, &msg); if (rc < 0) { remove_wait_queue(&bl_wq, &wq); rv = ERR_PTR(rc); diff --git a/fs/nfs/blocklayout/blocklayoutdm.c b/fs/nfs/blocklayout/blocklayoutdm.c index d055c7558073..3c38244a8724 100644 --- a/fs/nfs/blocklayout/blocklayoutdm.c +++ b/fs/nfs/blocklayout/blocklayoutdm.c @@ -66,7 +66,7 @@ static void dev_remove(dev_t dev) msg.len = sizeof(bl_msg) + bl_msg.totallen; add_wait_queue(&bl_wq, &wq); - if (rpc_queue_upcall(bl_device_pipe->d_inode, &msg) < 0) { + if (rpc_queue_upcall(RPC_I(bl_device_pipe->d_inode)->pipe, &msg) < 0) { remove_wait_queue(&bl_wq, &wq); goto out; } diff --git a/fs/nfs/idmap.c b/fs/nfs/idmap.c index 2c05f1991e1e..3c63c47c793d 100644 --- a/fs/nfs/idmap.c +++ b/fs/nfs/idmap.c @@ -589,7 +589,7 @@ nfs_idmap_id(struct idmap *idmap, struct idmap_hashtable *h, msg.len = sizeof(*im); add_wait_queue(&idmap->idmap_wq, &wq); - if (rpc_queue_upcall(idmap->idmap_dentry->d_inode, &msg) < 0) { + if (rpc_queue_upcall(RPC_I(idmap->idmap_dentry->d_inode)->pipe, &msg) < 0) { remove_wait_queue(&idmap->idmap_wq, &wq); goto out; } @@ -650,7 +650,7 @@ nfs_idmap_name(struct idmap *idmap, struct idmap_hashtable *h, add_wait_queue(&idmap->idmap_wq, &wq); - if (rpc_queue_upcall(idmap->idmap_dentry->d_inode, &msg) < 0) { + if (rpc_queue_upcall(RPC_I(idmap->idmap_dentry->d_inode)->pipe, &msg) < 0) { remove_wait_queue(&idmap->idmap_wq, &wq); goto out; } diff --git a/include/linux/sunrpc/rpc_pipe_fs.h b/include/linux/sunrpc/rpc_pipe_fs.h index 57512c23d34c..3ebc257e2b43 100644 --- a/include/linux/sunrpc/rpc_pipe_fs.h +++ b/include/linux/sunrpc/rpc_pipe_fs.h @@ -64,7 +64,7 @@ extern void rpc_put_sb_net(const struct net *net); extern ssize_t rpc_pipe_generic_upcall(struct file *, struct rpc_pipe_msg *, char __user *, size_t); -extern int rpc_queue_upcall(struct inode *, struct rpc_pipe_msg *); +extern int rpc_queue_upcall(struct rpc_pipe *, struct rpc_pipe_msg *); struct rpc_clnt; extern struct dentry *rpc_create_client_dir(struct dentry *, struct qstr *, struct rpc_clnt *); diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index e933484e55ef..4157e3151581 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c @@ -473,8 +473,7 @@ gss_setup_upcall(struct rpc_clnt *clnt, struct gss_auth *gss_auth, struct rpc_cr return gss_new; gss_msg = gss_add_msg(gss_new); if (gss_msg == gss_new) { - struct inode *inode = &gss_new->inode->vfs_inode; - int res = rpc_queue_upcall(inode, &gss_new->msg); + int res = rpc_queue_upcall(gss_new->inode->pipe, &gss_new->msg); if (res) { gss_unhash_msg(gss_new); gss_msg = ERR_PTR(res); diff --git a/net/sunrpc/rpc_pipe.c b/net/sunrpc/rpc_pipe.c index 299b1a3c3e49..4093da79d512 100644 --- a/net/sunrpc/rpc_pipe.c +++ b/net/sunrpc/rpc_pipe.c @@ -130,9 +130,8 @@ EXPORT_SYMBOL_GPL(rpc_pipe_generic_upcall); * initialize the fields of @msg (other than @msg->list) appropriately. */ int -rpc_queue_upcall(struct inode *inode, struct rpc_pipe_msg *msg) +rpc_queue_upcall(struct rpc_pipe *pipe, struct rpc_pipe_msg *msg) { - struct rpc_pipe *pipe = RPC_I(inode)->pipe; int res = -EPIPE; spin_lock(&pipe->lock); -- cgit v1.2.3 From c239d83b9921b8a8005a3bcd23000cfe18acf5c2 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Mon, 26 Dec 2011 15:44:06 +0300 Subject: SUNRPC: split SUNPRC PipeFS dentry and private pipe data creation This patch is a final step towards to removing PipeFS inode references from kernel code other than PipeFS itself. It makes all kernel SUNRPC PipeFS users depends on pipe private data, which state depend on their specific operations, etc. This patch completes SUNRPC PipeFS preparations and allows to create pipe private data and PipeFS dentries independently. Next step will be making SUNPRC PipeFS dentries allocated by SUNRPC PipeFS network namespace aware routines. Signed-off-by: Stanislav Kinsbursky Signed-off-by: Trond Myklebust --- fs/nfs/blocklayout/blocklayout.c | 16 ++++++++--- fs/nfs/blocklayout/blocklayout.h | 2 +- fs/nfs/blocklayout/blocklayoutdev.c | 2 +- fs/nfs/blocklayout/blocklayoutdm.c | 2 +- fs/nfs/idmap.c | 28 +++++++++++++------ include/linux/sunrpc/rpc_pipe_fs.h | 7 +++-- net/sunrpc/auth_gss/auth_gss.c | 54 ++++++++++++++++++++++++------------- net/sunrpc/rpc_pipe.c | 54 +++++++++++++++++++++---------------- 8 files changed, 107 insertions(+), 58 deletions(-) (limited to 'include/linux') diff --git a/fs/nfs/blocklayout/blocklayout.c b/fs/nfs/blocklayout/blocklayout.c index 48cfac31f64c..848660fd58c4 100644 --- a/fs/nfs/blocklayout/blocklayout.c +++ b/fs/nfs/blocklayout/blocklayout.c @@ -46,7 +46,7 @@ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Andy Adamson "); MODULE_DESCRIPTION("The NFSv4.1 pNFS Block layout driver"); -struct dentry *bl_device_pipe; +struct rpc_pipe *bl_device_pipe; wait_queue_head_t bl_wq; static void print_page(struct page *page) @@ -1051,16 +1051,23 @@ static int __init nfs4blocklayout_init(void) if (ret) goto out_putrpc; - bl_device_pipe = rpc_mkpipe(path.dentry, "blocklayout", NULL, - &bl_upcall_ops, 0); + bl_device_pipe = rpc_mkpipe_data(&bl_upcall_ops, 0); path_put(&path); if (IS_ERR(bl_device_pipe)) { ret = PTR_ERR(bl_device_pipe); goto out_putrpc; } + bl_device_pipe->dentry = rpc_mkpipe_dentry(path.dentry, "blocklayout", + NULL, bl_device_pipe); + if (IS_ERR(bl_device_pipe->dentry)) { + ret = PTR_ERR(bl_device_pipe->dentry); + goto out_destroy_pipe; + } out: return ret; +out_destroy_pipe: + rpc_destroy_pipe_data(bl_device_pipe); out_putrpc: rpc_put_mount(); out_remove: @@ -1074,7 +1081,8 @@ static void __exit nfs4blocklayout_exit(void) __func__); pnfs_unregister_layoutdriver(&blocklayout_type); - rpc_unlink(bl_device_pipe); + rpc_unlink(bl_device_pipe->dentry); + rpc_destroy_pipe_data(bl_device_pipe); rpc_put_mount(); } diff --git a/fs/nfs/blocklayout/blocklayout.h b/fs/nfs/blocklayout/blocklayout.h index e31a2df28e70..49c670b18a9e 100644 --- a/fs/nfs/blocklayout/blocklayout.h +++ b/fs/nfs/blocklayout/blocklayout.h @@ -161,7 +161,7 @@ struct bl_msg_hdr { u16 totallen; /* length of entire message, including hdr itself */ }; -extern struct dentry *bl_device_pipe; +extern struct rpc_pipe *bl_device_pipe; extern wait_queue_head_t bl_wq; #define BL_DEVICE_UMOUNT 0x0 /* Umount--delete devices */ diff --git a/fs/nfs/blocklayout/blocklayoutdev.c b/fs/nfs/blocklayout/blocklayoutdev.c index 81019190e46d..949b62478799 100644 --- a/fs/nfs/blocklayout/blocklayoutdev.c +++ b/fs/nfs/blocklayout/blocklayoutdev.c @@ -146,7 +146,7 @@ nfs4_blk_decode_device(struct nfs_server *server, dprintk("%s CALLING USERSPACE DAEMON\n", __func__); add_wait_queue(&bl_wq, &wq); - rc = rpc_queue_upcall(RPC_I(bl_device_pipe->d_inode)->pipe, &msg); + rc = rpc_queue_upcall(bl_device_pipe, &msg); if (rc < 0) { remove_wait_queue(&bl_wq, &wq); rv = ERR_PTR(rc); diff --git a/fs/nfs/blocklayout/blocklayoutdm.c b/fs/nfs/blocklayout/blocklayoutdm.c index 3c38244a8724..631f254d12ab 100644 --- a/fs/nfs/blocklayout/blocklayoutdm.c +++ b/fs/nfs/blocklayout/blocklayoutdm.c @@ -66,7 +66,7 @@ static void dev_remove(dev_t dev) msg.len = sizeof(bl_msg) + bl_msg.totallen; add_wait_queue(&bl_wq, &wq); - if (rpc_queue_upcall(RPC_I(bl_device_pipe->d_inode)->pipe, &msg) < 0) { + if (rpc_queue_upcall(bl_device_pipe, &msg) < 0) { remove_wait_queue(&bl_wq, &wq); goto out; } diff --git a/fs/nfs/idmap.c b/fs/nfs/idmap.c index 3c63c47c793d..2992cb854e12 100644 --- a/fs/nfs/idmap.c +++ b/fs/nfs/idmap.c @@ -410,7 +410,7 @@ struct idmap_hashtable { }; struct idmap { - struct dentry *idmap_dentry; + struct rpc_pipe *idmap_pipe; wait_queue_head_t idmap_wq; struct idmap_msg idmap_im; struct mutex idmap_lock; /* Serializes upcalls */ @@ -435,6 +435,7 @@ int nfs_idmap_new(struct nfs_client *clp) { struct idmap *idmap; + struct rpc_pipe *pipe; int error; BUG_ON(clp->cl_idmap != NULL); @@ -443,14 +444,23 @@ nfs_idmap_new(struct nfs_client *clp) if (idmap == NULL) return -ENOMEM; - idmap->idmap_dentry = rpc_mkpipe(clp->cl_rpcclient->cl_path.dentry, - "idmap", idmap, &idmap_upcall_ops, 0); - if (IS_ERR(idmap->idmap_dentry)) { - error = PTR_ERR(idmap->idmap_dentry); + pipe = rpc_mkpipe_data(&idmap_upcall_ops, 0); + if (IS_ERR(pipe)) { + error = PTR_ERR(pipe); kfree(idmap); return error; } + if (clp->cl_rpcclient->cl_path.dentry) + pipe->dentry = rpc_mkpipe_dentry(clp->cl_rpcclient->cl_path.dentry, + "idmap", idmap, pipe); + if (IS_ERR(pipe->dentry)) { + error = PTR_ERR(pipe->dentry); + rpc_destroy_pipe_data(pipe); + kfree(idmap); + return error; + } + idmap->idmap_pipe = pipe; mutex_init(&idmap->idmap_lock); mutex_init(&idmap->idmap_im_lock); init_waitqueue_head(&idmap->idmap_wq); @@ -468,7 +478,9 @@ nfs_idmap_delete(struct nfs_client *clp) if (!idmap) return; - rpc_unlink(idmap->idmap_dentry); + if (idmap->idmap_pipe->dentry) + rpc_unlink(idmap->idmap_pipe->dentry); + rpc_destroy_pipe_data(idmap->idmap_pipe); clp->cl_idmap = NULL; kfree(idmap); } @@ -589,7 +601,7 @@ nfs_idmap_id(struct idmap *idmap, struct idmap_hashtable *h, msg.len = sizeof(*im); add_wait_queue(&idmap->idmap_wq, &wq); - if (rpc_queue_upcall(RPC_I(idmap->idmap_dentry->d_inode)->pipe, &msg) < 0) { + if (rpc_queue_upcall(idmap->idmap_pipe, &msg) < 0) { remove_wait_queue(&idmap->idmap_wq, &wq); goto out; } @@ -650,7 +662,7 @@ nfs_idmap_name(struct idmap *idmap, struct idmap_hashtable *h, add_wait_queue(&idmap->idmap_wq, &wq); - if (rpc_queue_upcall(RPC_I(idmap->idmap_dentry->d_inode)->pipe, &msg) < 0) { + if (rpc_queue_upcall(idmap->idmap_pipe, &msg) < 0) { remove_wait_queue(&idmap->idmap_wq, &wq); goto out; } diff --git a/include/linux/sunrpc/rpc_pipe_fs.h b/include/linux/sunrpc/rpc_pipe_fs.h index 3ebc257e2b43..0d1f748f76da 100644 --- a/include/linux/sunrpc/rpc_pipe_fs.h +++ b/include/linux/sunrpc/rpc_pipe_fs.h @@ -34,6 +34,7 @@ struct rpc_pipe { struct delayed_work queue_timeout; const struct rpc_pipe_ops *ops; spinlock_t lock; + struct dentry *dentry; }; struct rpc_inode { @@ -77,8 +78,10 @@ extern struct dentry *rpc_create_cache_dir(struct dentry *, struct cache_detail *); extern void rpc_remove_cache_dir(struct dentry *); -extern struct dentry *rpc_mkpipe(struct dentry *, const char *, void *, - const struct rpc_pipe_ops *, int flags); +struct rpc_pipe *rpc_mkpipe_data(const struct rpc_pipe_ops *ops, int flags); +void rpc_destroy_pipe_data(struct rpc_pipe *pipe); +extern struct dentry *rpc_mkpipe_dentry(struct dentry *, const char *, void *, + struct rpc_pipe *); extern int rpc_unlink(struct dentry *); extern struct vfsmount *rpc_get_mount(void); extern void rpc_put_mount(void); diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index 304b8309f217..f684ce606667 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c @@ -81,7 +81,7 @@ struct gss_auth { * mechanism (for example, "krb5") and exists for * backwards-compatibility with older gssd's. */ - struct dentry *dentry[2]; + struct rpc_pipe *pipe[2]; }; /* pipe_version >= 0 if and only if someone has a pipe open. */ @@ -449,7 +449,7 @@ gss_alloc_msg(struct gss_auth *gss_auth, struct rpc_clnt *clnt, kfree(gss_msg); return ERR_PTR(vers); } - gss_msg->pipe = RPC_I(gss_auth->dentry[vers]->d_inode)->pipe; + gss_msg->pipe = gss_auth->pipe[vers]; INIT_LIST_HEAD(&gss_msg->list); rpc_init_wait_queue(&gss_msg->rpc_waitqueue, "RPCSEC_GSS upcall waitq"); init_waitqueue_head(&gss_msg->waitqueue); @@ -799,21 +799,33 @@ gss_create(struct rpc_clnt *clnt, rpc_authflavor_t flavor) * that we supported only the old pipe. So we instead create * the new pipe first. */ - gss_auth->dentry[1] = rpc_mkpipe(clnt->cl_path.dentry, - "gssd", - clnt, &gss_upcall_ops_v1, - RPC_PIPE_WAIT_FOR_OPEN); - if (IS_ERR(gss_auth->dentry[1])) { - err = PTR_ERR(gss_auth->dentry[1]); + gss_auth->pipe[1] = rpc_mkpipe_data(&gss_upcall_ops_v1, + RPC_PIPE_WAIT_FOR_OPEN); + if (IS_ERR(gss_auth->pipe[1])) { + err = PTR_ERR(gss_auth->pipe[1]); goto err_put_mech; } - gss_auth->dentry[0] = rpc_mkpipe(clnt->cl_path.dentry, - gss_auth->mech->gm_name, - clnt, &gss_upcall_ops_v0, - RPC_PIPE_WAIT_FOR_OPEN); - if (IS_ERR(gss_auth->dentry[0])) { - err = PTR_ERR(gss_auth->dentry[0]); + gss_auth->pipe[0] = rpc_mkpipe_data(&gss_upcall_ops_v0, + RPC_PIPE_WAIT_FOR_OPEN); + if (IS_ERR(gss_auth->pipe[0])) { + err = PTR_ERR(gss_auth->pipe[0]); + goto err_destroy_pipe_1; + } + + gss_auth->pipe[1]->dentry = rpc_mkpipe_dentry(clnt->cl_path.dentry, + "gssd", + clnt, gss_auth->pipe[1]); + if (IS_ERR(gss_auth->pipe[1]->dentry)) { + err = PTR_ERR(gss_auth->pipe[1]->dentry); + goto err_destroy_pipe_0; + } + + gss_auth->pipe[0]->dentry = rpc_mkpipe_dentry(clnt->cl_path.dentry, + gss_auth->mech->gm_name, + clnt, gss_auth->pipe[0]); + if (IS_ERR(gss_auth->pipe[0]->dentry)) { + err = PTR_ERR(gss_auth->pipe[0]->dentry); goto err_unlink_pipe_1; } err = rpcauth_init_credcache(auth); @@ -822,9 +834,13 @@ gss_create(struct rpc_clnt *clnt, rpc_authflavor_t flavor) return auth; err_unlink_pipe_0: - rpc_unlink(gss_auth->dentry[0]); + rpc_unlink(gss_auth->pipe[0]->dentry); err_unlink_pipe_1: - rpc_unlink(gss_auth->dentry[1]); + rpc_unlink(gss_auth->pipe[1]->dentry); +err_destroy_pipe_0: + rpc_destroy_pipe_data(gss_auth->pipe[0]); +err_destroy_pipe_1: + rpc_destroy_pipe_data(gss_auth->pipe[1]); err_put_mech: gss_mech_put(gss_auth->mech); err_free: @@ -837,8 +853,10 @@ out_dec: static void gss_free(struct gss_auth *gss_auth) { - rpc_unlink(gss_auth->dentry[1]); - rpc_unlink(gss_auth->dentry[0]); + rpc_unlink(gss_auth->pipe[0]->dentry); + rpc_unlink(gss_auth->pipe[1]->dentry); + rpc_destroy_pipe_data(gss_auth->pipe[0]); + rpc_destroy_pipe_data(gss_auth->pipe[1]); gss_mech_put(gss_auth->mech); kfree(gss_auth); diff --git a/net/sunrpc/rpc_pipe.c b/net/sunrpc/rpc_pipe.c index 4093da79d512..6dd8b96e8df7 100644 --- a/net/sunrpc/rpc_pipe.c +++ b/net/sunrpc/rpc_pipe.c @@ -206,7 +206,6 @@ static void rpc_i_callback(struct rcu_head *head) { struct inode *inode = container_of(head, struct inode, i_rcu); - kfree(RPC_I(inode)->pipe); kmem_cache_free(rpc_inode_cachep, RPC_I(inode)); } @@ -575,34 +574,44 @@ init_pipe(struct rpc_pipe *pipe) rpc_timeout_upcall_queue); pipe->ops = NULL; spin_lock_init(&pipe->lock); + pipe->dentry = NULL; +} +void rpc_destroy_pipe_data(struct rpc_pipe *pipe) +{ + kfree(pipe); } +EXPORT_SYMBOL_GPL(rpc_destroy_pipe_data); -static int __rpc_mkpipe(struct inode *dir, struct dentry *dentry, - umode_t mode, - const struct file_operations *i_fop, - void *private, - const struct rpc_pipe_ops *ops, - int flags) +struct rpc_pipe *rpc_mkpipe_data(const struct rpc_pipe_ops *ops, int flags) { struct rpc_pipe *pipe; - struct rpc_inode *rpci; - int err; pipe = kzalloc(sizeof(struct rpc_pipe), GFP_KERNEL); if (!pipe) - return -ENOMEM; + return ERR_PTR(-ENOMEM); init_pipe(pipe); + pipe->ops = ops; + pipe->flags = flags; + return pipe; +} +EXPORT_SYMBOL_GPL(rpc_mkpipe_data); + +static int __rpc_mkpipe_dentry(struct inode *dir, struct dentry *dentry, + umode_t mode, + const struct file_operations *i_fop, + void *private, + struct rpc_pipe *pipe) +{ + struct rpc_inode *rpci; + int err; + err = __rpc_create_common(dir, dentry, S_IFIFO | mode, i_fop, private); - if (err) { - kfree(pipe); + if (err) return err; - } rpci = RPC_I(dentry->d_inode); rpci->private = private; rpci->pipe = pipe; - rpci->pipe->flags = flags; - rpci->pipe->ops = ops; fsnotify_create(dir, dentry); return 0; } @@ -819,9 +828,8 @@ static int rpc_rmdir_depopulate(struct dentry *dentry, * The @private argument passed here will be available to all these methods * from the file pointer, via RPC_I(file->f_dentry->d_inode)->private. */ -struct dentry *rpc_mkpipe(struct dentry *parent, const char *name, - void *private, const struct rpc_pipe_ops *ops, - int flags) +struct dentry *rpc_mkpipe_dentry(struct dentry *parent, const char *name, + void *private, struct rpc_pipe *pipe) { struct dentry *dentry; struct inode *dir = parent->d_inode; @@ -829,9 +837,9 @@ struct dentry *rpc_mkpipe(struct dentry *parent, const char *name, struct qstr q; int err; - if (ops->upcall == NULL) + if (pipe->ops->upcall == NULL) umode &= ~S_IRUGO; - if (ops->downcall == NULL) + if (pipe->ops->downcall == NULL) umode &= ~S_IWUGO; q.name = name; @@ -842,8 +850,8 @@ struct dentry *rpc_mkpipe(struct dentry *parent, const char *name, dentry = __rpc_lookup_create_exclusive(parent, &q); if (IS_ERR(dentry)) goto out; - err = __rpc_mkpipe(dir, dentry, umode, &rpc_pipe_fops, - private, ops, flags); + err = __rpc_mkpipe_dentry(dir, dentry, umode, &rpc_pipe_fops, + private, pipe); if (err) goto out_err; out: @@ -856,7 +864,7 @@ out_err: err); goto out; } -EXPORT_SYMBOL_GPL(rpc_mkpipe); +EXPORT_SYMBOL_GPL(rpc_mkpipe_dentry); /** * rpc_unlink - remove a pipe -- cgit v1.2.3 From 0157d021d23a087eecfa830502f81cfe843f0d16 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Wed, 11 Jan 2012 19:18:01 +0400 Subject: SUNRPC: handle RPC client pipefs dentries by network namespace aware routines v2: 1) "Over-put" of PipeFS mount point fixed. Fix is ugly, but allows to bisect the patch set. And it will be removed later in the series. This patch makes RPC clients PipeFs dentries allocations in it's owner network namespace context. RPC client pipefs dentries creation logic has been changed: 1) Pipefs dentries creation by sb was moved to separated function, which will be used for handling PipeFS mount notification. 2) Initial value of RPC client PipeFS dir dentry is set no NULL now. RPC client pipefs dentries cleanup logic has been changed: 1) Cleanup is done now in separated rpc_remove_pipedir() function, which takes care about pipefs superblock locking. Also this patch removes slashes from cb_program.pipe_dir_name and from NFS_PIPE_DIRNAME to make rpc_d_lookup_sb() work. This doesn't affect vfs_path_lookup() results in nfs4blocklayout_init() since this slash is cutted off anyway in link_path_walk(). Signed-off-by: Stanislav Kinsbursky Signed-off-by: Trond Myklebust --- fs/nfsd/nfs4callback.c | 2 +- include/linux/nfs.h | 2 +- net/sunrpc/clnt.c | 97 +++++++++++++++++++++++++++++++++----------------- 3 files changed, 66 insertions(+), 35 deletions(-) (limited to 'include/linux') diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c index 6f3ebb48b12f..426ccb171650 100644 --- a/fs/nfsd/nfs4callback.c +++ b/fs/nfsd/nfs4callback.c @@ -622,7 +622,7 @@ static struct rpc_program cb_program = { .nrvers = ARRAY_SIZE(nfs_cb_version), .version = nfs_cb_version, .stats = &cb_stats, - .pipe_dir_name = "/nfsd4_cb", + .pipe_dir_name = "nfsd4_cb", }; static int max_cb_time(void) diff --git a/include/linux/nfs.h b/include/linux/nfs.h index 8c6ee44914cb..6d1fb63f5922 100644 --- a/include/linux/nfs.h +++ b/include/linux/nfs.h @@ -29,7 +29,7 @@ #define NFS_MNT_VERSION 1 #define NFS_MNT3_VERSION 3 -#define NFS_PIPE_DIRNAME "/nfs" +#define NFS_PIPE_DIRNAME "nfs" /* * NFS stats. The good thing with these values is that NFSv3 errors are diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index f0268ea7e711..5ef192c1a57c 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -93,52 +93,89 @@ static void rpc_unregister_client(struct rpc_clnt *clnt) spin_unlock(&rpc_client_lock); } -static int -rpc_setup_pipedir(struct rpc_clnt *clnt, char *dir_name) +static void __rpc_clnt_remove_pipedir(struct rpc_clnt *clnt) +{ + if (clnt->cl_path.dentry) + rpc_remove_client_dir(clnt->cl_path.dentry); + clnt->cl_path.dentry = NULL; +} + +static void rpc_clnt_remove_pipedir(struct rpc_clnt *clnt) +{ + struct super_block *pipefs_sb; + int put_mnt = 0; + + pipefs_sb = rpc_get_sb_net(clnt->cl_xprt->xprt_net); + if (pipefs_sb) { + if (clnt->cl_path.dentry) + put_mnt = 1; + __rpc_clnt_remove_pipedir(clnt); + rpc_put_sb_net(clnt->cl_xprt->xprt_net); + } + if (put_mnt) + rpc_put_mount(); +} + +static struct dentry *rpc_setup_pipedir_sb(struct super_block *sb, + struct rpc_clnt *clnt, char *dir_name) { static uint32_t clntid; - struct path path, dir; char name[15]; struct qstr q = { .name = name, }; + struct dentry *dir, *dentry; int error; - clnt->cl_path.mnt = ERR_PTR(-ENOENT); - clnt->cl_path.dentry = ERR_PTR(-ENOENT); - if (dir_name == NULL) - return 0; - - path.mnt = rpc_get_mount(); - if (IS_ERR(path.mnt)) - return PTR_ERR(path.mnt); - error = vfs_path_lookup(path.mnt->mnt_root, path.mnt, dir_name, 0, &dir); - if (error) - goto err; - + dir = rpc_d_lookup_sb(sb, dir_name); + if (dir == NULL) + return dir; for (;;) { q.len = snprintf(name, sizeof(name), "clnt%x", (unsigned int)clntid++); name[sizeof(name) - 1] = '\0'; q.hash = full_name_hash(q.name, q.len); - path.dentry = rpc_create_client_dir(dir.dentry, &q, clnt); - if (!IS_ERR(path.dentry)) + dentry = rpc_create_client_dir(dir, &q, clnt); + if (!IS_ERR(dentry)) break; - error = PTR_ERR(path.dentry); + error = PTR_ERR(dentry); if (error != -EEXIST) { printk(KERN_INFO "RPC: Couldn't create pipefs entry" " %s/%s, error %d\n", dir_name, name, error); - goto err_path_put; + break; } } - path_put(&dir); + dput(dir); + return dentry; +} + +static int +rpc_setup_pipedir(struct rpc_clnt *clnt, char *dir_name) +{ + struct super_block *pipefs_sb; + struct path path; + + clnt->cl_path.mnt = ERR_PTR(-ENOENT); + clnt->cl_path.dentry = NULL; + if (dir_name == NULL) + return 0; + + path.mnt = rpc_get_mount(); + if (IS_ERR(path.mnt)) + return PTR_ERR(path.mnt); + pipefs_sb = rpc_get_sb_net(clnt->cl_xprt->xprt_net); + if (!pipefs_sb) { + rpc_put_mount(); + return -ENOENT; + } + path.dentry = rpc_setup_pipedir_sb(pipefs_sb, clnt, dir_name); + rpc_put_sb_net(clnt->cl_xprt->xprt_net); + if (IS_ERR(path.dentry)) { + rpc_put_mount(); + return PTR_ERR(path.dentry); + } clnt->cl_path = path; return 0; -err_path_put: - path_put(&dir); -err: - rpc_put_mount(); - return error; } static struct rpc_clnt * rpc_new_client(const struct rpc_create_args *args, struct rpc_xprt *xprt) @@ -246,10 +283,7 @@ static struct rpc_clnt * rpc_new_client(const struct rpc_create_args *args, stru return clnt; out_no_auth: - if (!IS_ERR(clnt->cl_path.dentry)) { - rpc_remove_client_dir(clnt->cl_path.dentry); - rpc_put_mount(); - } + rpc_clnt_remove_pipedir(clnt); out_no_path: kfree(clnt->cl_principal); out_no_principal: @@ -474,10 +508,7 @@ rpc_free_client(struct rpc_clnt *clnt) { dprintk("RPC: destroying %s client for %s\n", clnt->cl_protname, clnt->cl_server); - if (!IS_ERR(clnt->cl_path.dentry)) { - rpc_remove_client_dir(clnt->cl_path.dentry); - rpc_put_mount(); - } + rpc_clnt_remove_pipedir(clnt); if (clnt->cl_parent != clnt) { rpc_release_client(clnt->cl_parent); goto out_free; -- cgit v1.2.3 From 70abc49b4f4a4ef04a6bd9852edbd047b480bed7 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Thu, 12 Jan 2012 22:07:51 +0400 Subject: SUNRPC: make SUNPRC clients list per network namespace context This patch moves static SUNRPC clients list and it's lock to sunrpc_net structure. Currently this list is used only for debug purposes. But later it will be used also for selecting clients by networks namespace on PipeFS mount/umount events. Per-network namespace lists will make this faster and simplier. Note: client list is taken from "init_net" network namespace context in rpc_show_tasks(). This will be changed some day later with making SUNRPC sysctl's per network namespace context. Signed-off-by: Stanislav Kinsbursky Signed-off-by: Trond Myklebust --- include/linux/sunrpc/sched.h | 3 ++- net/sunrpc/clnt.c | 26 +++++++++++++++----------- net/sunrpc/netns.h | 3 +++ net/sunrpc/sunrpc_syms.c | 3 +++ net/sunrpc/sysctl.c | 4 +++- 5 files changed, 26 insertions(+), 13 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sunrpc/sched.h b/include/linux/sunrpc/sched.h index e7756896f3ca..b16243a35f0b 100644 --- a/include/linux/sunrpc/sched.h +++ b/include/linux/sunrpc/sched.h @@ -244,7 +244,8 @@ int rpciod_up(void); void rpciod_down(void); int __rpc_wait_for_completion_task(struct rpc_task *task, int (*)(void *)); #ifdef RPC_DEBUG -void rpc_show_tasks(void); +struct net; +void rpc_show_tasks(struct net *); #endif int rpc_init_mempool(void); void rpc_destroy_mempool(void); diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index 5ef192c1a57c..90e82c5daeb6 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -38,6 +38,7 @@ #include #include "sunrpc.h" +#include "netns.h" #ifdef RPC_DEBUG # define RPCDBG_FACILITY RPCDBG_CALL @@ -50,8 +51,6 @@ /* * All RPC clients are linked into this list */ -static LIST_HEAD(all_clients); -static DEFINE_SPINLOCK(rpc_client_lock); static DECLARE_WAIT_QUEUE_HEAD(destroy_wait); @@ -81,16 +80,20 @@ static int rpc_ping(struct rpc_clnt *clnt); static void rpc_register_client(struct rpc_clnt *clnt) { - spin_lock(&rpc_client_lock); - list_add(&clnt->cl_clients, &all_clients); - spin_unlock(&rpc_client_lock); + struct sunrpc_net *sn = net_generic(clnt->cl_xprt->xprt_net, sunrpc_net_id); + + spin_lock(&sn->rpc_client_lock); + list_add(&clnt->cl_clients, &sn->all_clients); + spin_unlock(&sn->rpc_client_lock); } static void rpc_unregister_client(struct rpc_clnt *clnt) { - spin_lock(&rpc_client_lock); + struct sunrpc_net *sn = net_generic(clnt->cl_xprt->xprt_net, sunrpc_net_id); + + spin_lock(&sn->rpc_client_lock); list_del(&clnt->cl_clients); - spin_unlock(&rpc_client_lock); + spin_unlock(&sn->rpc_client_lock); } static void __rpc_clnt_remove_pipedir(struct rpc_clnt *clnt) @@ -1883,14 +1886,15 @@ static void rpc_show_task(const struct rpc_clnt *clnt, task->tk_action, rpc_waitq); } -void rpc_show_tasks(void) +void rpc_show_tasks(struct net *net) { struct rpc_clnt *clnt; struct rpc_task *task; int header = 0; + struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); - spin_lock(&rpc_client_lock); - list_for_each_entry(clnt, &all_clients, cl_clients) { + spin_lock(&sn->rpc_client_lock); + list_for_each_entry(clnt, &sn->all_clients, cl_clients) { spin_lock(&clnt->cl_lock); list_for_each_entry(task, &clnt->cl_tasks, tk_task) { if (!header) { @@ -1901,6 +1905,6 @@ void rpc_show_tasks(void) } spin_unlock(&clnt->cl_lock); } - spin_unlock(&rpc_client_lock); + spin_unlock(&sn->rpc_client_lock); } #endif diff --git a/net/sunrpc/netns.h b/net/sunrpc/netns.h index 11d2f4863403..0f3af34fa502 100644 --- a/net/sunrpc/netns.h +++ b/net/sunrpc/netns.h @@ -12,6 +12,9 @@ struct sunrpc_net { struct super_block *pipefs_sb; struct mutex pipefs_sb_lock; + + struct list_head all_clients; + spinlock_t rpc_client_lock; }; extern int sunrpc_net_id; diff --git a/net/sunrpc/sunrpc_syms.c b/net/sunrpc/sunrpc_syms.c index 7086d11589c8..b4217dc8599c 100644 --- a/net/sunrpc/sunrpc_syms.c +++ b/net/sunrpc/sunrpc_syms.c @@ -29,6 +29,7 @@ int sunrpc_net_id; static __net_init int sunrpc_init_net(struct net *net) { int err; + struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); err = rpc_proc_init(net); if (err) @@ -39,6 +40,8 @@ static __net_init int sunrpc_init_net(struct net *net) goto err_ipmap; rpc_pipefs_init_net(net); + INIT_LIST_HEAD(&sn->all_clients); + spin_lock_init(&sn->rpc_client_lock); return 0; err_ipmap: diff --git a/net/sunrpc/sysctl.c b/net/sunrpc/sysctl.c index e65dcc613339..af7d339add9d 100644 --- a/net/sunrpc/sysctl.c +++ b/net/sunrpc/sysctl.c @@ -20,6 +20,8 @@ #include #include +#include "netns.h" + /* * Declare the debug flags here */ @@ -110,7 +112,7 @@ proc_dodebug(ctl_table *table, int write, *(unsigned int *) table->data = value; /* Display the RPC tasks on writing to rpc_debug */ if (strcmp(table->procname, "rpc_debug") == 0) - rpc_show_tasks(); + rpc_show_tasks(&init_net); } else { if (!access_ok(VERIFY_WRITE, buffer, left)) return -EFAULT; -- cgit v1.2.3 From 80df9d202255071c8ec610a6a3fdca5cac69f7bd Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Wed, 11 Jan 2012 19:18:17 +0400 Subject: SUNRPC: subscribe RPC clients to pipefs notifications This patch subscribes RPC clients to RPC pipefs notifications. RPC clients notifier block is registering with pipefs initialization during SUNRPC module init. This notifier callback is responsible for RPC client PipeFS directory and GSS pipes creation. For pipes creation and destruction two additional callbacks were added to struct rpc_authops. Note that no locking required in notifier callback because PipeFS superblock pointer is passed as an argument from it's creation or destruction routine and thus we can be sure about it's validity. Signed-off-by: Stanislav Kinsbursky Signed-off-by: Trond Myklebust --- include/linux/sunrpc/auth.h | 2 ++ net/sunrpc/auth_gss/auth_gss.c | 10 ++++-- net/sunrpc/clnt.c | 69 +++++++++++++++++++++++++++++++++++++++++- net/sunrpc/rpc_pipe.c | 19 ++++++++---- net/sunrpc/sunrpc.h | 2 ++ 5 files changed, 92 insertions(+), 10 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sunrpc/auth.h b/include/linux/sunrpc/auth.h index 7874a8a56638..492a36d72829 100644 --- a/include/linux/sunrpc/auth.h +++ b/include/linux/sunrpc/auth.h @@ -99,6 +99,8 @@ struct rpc_authops { struct rpc_cred * (*lookup_cred)(struct rpc_auth *, struct auth_cred *, int); struct rpc_cred * (*crcreate)(struct rpc_auth*, struct auth_cred *, int); + int (*pipes_create)(struct rpc_auth *); + void (*pipes_destroy)(struct rpc_auth *); }; struct rpc_credops { diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index 1a6fa9173519..9da2d837b512 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c @@ -762,8 +762,10 @@ static void gss_pipes_dentries_destroy(struct rpc_auth *auth) struct gss_auth *gss_auth; gss_auth = container_of(auth, struct gss_auth, rpc_auth); - rpc_unlink(gss_auth->pipe[0]->dentry); - rpc_unlink(gss_auth->pipe[1]->dentry); + if (gss_auth->pipe[0]->dentry) + rpc_unlink(gss_auth->pipe[0]->dentry); + if (gss_auth->pipe[1]->dentry) + rpc_unlink(gss_auth->pipe[1]->dentry); } static int gss_pipes_dentries_create(struct rpc_auth *auth) @@ -1614,7 +1616,9 @@ static const struct rpc_authops authgss_ops = { .create = gss_create, .destroy = gss_destroy, .lookup_cred = gss_lookup_cred, - .crcreate = gss_create_cred + .crcreate = gss_create_cred, + .pipes_create = gss_pipes_dentries_create, + .pipes_destroy = gss_pipes_dentries_destroy, }; static const struct rpc_credops gss_credops = { diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index 90e82c5daeb6..417074500592 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -98,8 +98,11 @@ static void rpc_unregister_client(struct rpc_clnt *clnt) static void __rpc_clnt_remove_pipedir(struct rpc_clnt *clnt) { - if (clnt->cl_path.dentry) + if (clnt->cl_path.dentry) { + if (clnt->cl_auth && clnt->cl_auth->au_ops->pipes_destroy) + clnt->cl_auth->au_ops->pipes_destroy(clnt->cl_auth); rpc_remove_client_dir(clnt->cl_path.dentry); + } clnt->cl_path.dentry = NULL; } @@ -181,6 +184,70 @@ rpc_setup_pipedir(struct rpc_clnt *clnt, char *dir_name) return 0; } +static int __rpc_pipefs_event(struct rpc_clnt *clnt, unsigned long event, + struct super_block *sb) +{ + struct dentry *dentry; + int err = 0; + + switch (event) { + case RPC_PIPEFS_MOUNT: + if (clnt->cl_program->pipe_dir_name == NULL) + break; + dentry = rpc_setup_pipedir_sb(sb, clnt, + clnt->cl_program->pipe_dir_name); + BUG_ON(dentry == NULL); + if (IS_ERR(dentry)) + return PTR_ERR(dentry); + clnt->cl_path.dentry = dentry; + if (clnt->cl_auth->au_ops->pipes_create) { + err = clnt->cl_auth->au_ops->pipes_create(clnt->cl_auth); + if (err) + __rpc_clnt_remove_pipedir(clnt); + } + break; + case RPC_PIPEFS_UMOUNT: + __rpc_clnt_remove_pipedir(clnt); + break; + default: + printk(KERN_ERR "%s: unknown event: %ld\n", __func__, event); + return -ENOTSUPP; + } + return err; +} + +static int rpc_pipefs_event(struct notifier_block *nb, unsigned long event, + void *ptr) +{ + struct super_block *sb = ptr; + struct rpc_clnt *clnt; + int error = 0; + struct sunrpc_net *sn = net_generic(sb->s_fs_info, sunrpc_net_id); + + spin_lock(&sn->rpc_client_lock); + list_for_each_entry(clnt, &sn->all_clients, cl_clients) { + error = __rpc_pipefs_event(clnt, event, sb); + if (error) + break; + } + spin_unlock(&sn->rpc_client_lock); + return error; +} + +static struct notifier_block rpc_clients_block = { + .notifier_call = rpc_pipefs_event, +}; + +int rpc_clients_notifier_register(void) +{ + return rpc_pipefs_notifier_register(&rpc_clients_block); +} + +void rpc_clients_notifier_unregister(void) +{ + return rpc_pipefs_notifier_unregister(&rpc_clients_block); +} + static struct rpc_clnt * rpc_new_client(const struct rpc_create_args *args, struct rpc_xprt *xprt) { struct rpc_program *program = args->program; diff --git a/net/sunrpc/rpc_pipe.c b/net/sunrpc/rpc_pipe.c index 6dd8b96e8df7..910de4169a8d 100644 --- a/net/sunrpc/rpc_pipe.c +++ b/net/sunrpc/rpc_pipe.c @@ -937,7 +937,7 @@ struct dentry *rpc_create_client_dir(struct dentry *dentry, /** * rpc_remove_client_dir - Remove a directory created with rpc_create_client_dir() - * @dentry: directory to remove + * @clnt: rpc client */ int rpc_remove_client_dir(struct dentry *dentry) { @@ -1188,17 +1188,24 @@ int register_rpc_pipefs(void) init_once); if (!rpc_inode_cachep) return -ENOMEM; + err = rpc_clients_notifier_register(); + if (err) + goto err_notifier; err = register_filesystem(&rpc_pipe_fs_type); - if (err) { - kmem_cache_destroy(rpc_inode_cachep); - return err; - } - + if (err) + goto err_register; return 0; + +err_register: + rpc_clients_notifier_unregister(); +err_notifier: + kmem_cache_destroy(rpc_inode_cachep); + return err; } void unregister_rpc_pipefs(void) { + rpc_clients_notifier_unregister(); kmem_cache_destroy(rpc_inode_cachep); unregister_filesystem(&rpc_pipe_fs_type); } diff --git a/net/sunrpc/sunrpc.h b/net/sunrpc/sunrpc.h index 90c292e2738b..14c9f6d1c5ff 100644 --- a/net/sunrpc/sunrpc.h +++ b/net/sunrpc/sunrpc.h @@ -47,5 +47,7 @@ int svc_send_common(struct socket *sock, struct xdr_buf *xdr, struct page *headpage, unsigned long headoffset, struct page *tailpage, unsigned long tailoffset); +int rpc_clients_notifier_register(void); +void rpc_clients_notifier_unregister(void); #endif /* _NET_SUNRPC_SUNRPC_H */ -- cgit v1.2.3 From 30507f58ce11e7664512059c708347d7a7d75271 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Wed, 11 Jan 2012 19:18:42 +0400 Subject: SUNRPC: remove RPC PipeFS mount point reference from RPC client This is a cleanup patch. We don't need this reference anymore. Signed-off-by: Stanislav Kinsbursky Signed-off-by: Trond Myklebust --- fs/nfs/idmap.c | 4 ++-- include/linux/sunrpc/clnt.h | 2 +- net/sunrpc/auth_gss/auth_gss.c | 8 ++++---- net/sunrpc/clnt.c | 21 ++++++++++----------- 4 files changed, 17 insertions(+), 18 deletions(-) (limited to 'include/linux') diff --git a/fs/nfs/idmap.c b/fs/nfs/idmap.c index 2992cb854e12..588d7da5b17e 100644 --- a/fs/nfs/idmap.c +++ b/fs/nfs/idmap.c @@ -451,8 +451,8 @@ nfs_idmap_new(struct nfs_client *clp) return error; } - if (clp->cl_rpcclient->cl_path.dentry) - pipe->dentry = rpc_mkpipe_dentry(clp->cl_rpcclient->cl_path.dentry, + if (clp->cl_rpcclient->cl_dentry) + pipe->dentry = rpc_mkpipe_dentry(clp->cl_rpcclient->cl_dentry, "idmap", idmap, pipe); if (IS_ERR(pipe->dentry)) { error = PTR_ERR(pipe->dentry); diff --git a/include/linux/sunrpc/clnt.h b/include/linux/sunrpc/clnt.h index 2c5993a17c33..bfd61852b718 100644 --- a/include/linux/sunrpc/clnt.h +++ b/include/linux/sunrpc/clnt.h @@ -57,7 +57,7 @@ struct rpc_clnt { int cl_nodelen; /* nodename length */ char cl_nodename[UNX_MAXNODENAME]; - struct path cl_path; + struct dentry * cl_dentry; struct rpc_clnt * cl_parent; /* Points to parent of clones */ struct rpc_rtt cl_rtt_default; struct rpc_timeout cl_timeout_default; diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index 9da2d837b512..5ebb602cabe0 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c @@ -777,12 +777,12 @@ static int gss_pipes_dentries_create(struct rpc_auth *auth) gss_auth = container_of(auth, struct gss_auth, rpc_auth); clnt = gss_auth->client; - gss_auth->pipe[1]->dentry = rpc_mkpipe_dentry(clnt->cl_path.dentry, + gss_auth->pipe[1]->dentry = rpc_mkpipe_dentry(clnt->cl_dentry, "gssd", clnt, gss_auth->pipe[1]); if (IS_ERR(gss_auth->pipe[1]->dentry)) return PTR_ERR(gss_auth->pipe[1]->dentry); - gss_auth->pipe[0]->dentry = rpc_mkpipe_dentry(clnt->cl_path.dentry, + gss_auth->pipe[0]->dentry = rpc_mkpipe_dentry(clnt->cl_dentry, gss_auth->mech->gm_name, clnt, gss_auth->pipe[0]); if (IS_ERR(gss_auth->pipe[0]->dentry)) { @@ -804,7 +804,7 @@ static void gss_pipes_dentries_destroy_net(struct rpc_clnt *clnt, sb = rpc_get_sb_net(net); if (sb) { - if (clnt->cl_path.dentry) + if (clnt->cl_dentry) gss_pipes_dentries_destroy(auth); rpc_put_sb_net(net); } @@ -819,7 +819,7 @@ static int gss_pipes_dentries_create_net(struct rpc_clnt *clnt, sb = rpc_get_sb_net(net); if (sb) { - if (clnt->cl_path.dentry) + if (clnt->cl_dentry) err = gss_pipes_dentries_create(auth); rpc_put_sb_net(net); } diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index e3ced3061212..ed7c22de9319 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -98,12 +98,12 @@ static void rpc_unregister_client(struct rpc_clnt *clnt) static void __rpc_clnt_remove_pipedir(struct rpc_clnt *clnt) { - if (clnt->cl_path.dentry) { + if (clnt->cl_dentry) { if (clnt->cl_auth && clnt->cl_auth->au_ops->pipes_destroy) clnt->cl_auth->au_ops->pipes_destroy(clnt->cl_auth); - rpc_remove_client_dir(clnt->cl_path.dentry); + rpc_remove_client_dir(clnt->cl_dentry); } - clnt->cl_path.dentry = NULL; + clnt->cl_dentry = NULL; } static void rpc_clnt_remove_pipedir(struct rpc_clnt *clnt) @@ -154,20 +154,19 @@ static int rpc_setup_pipedir(struct rpc_clnt *clnt, char *dir_name) { struct super_block *pipefs_sb; - struct path path; + struct dentry *dentry; - clnt->cl_path.mnt = ERR_PTR(-ENOENT); - clnt->cl_path.dentry = NULL; + clnt->cl_dentry = NULL; if (dir_name == NULL) return 0; pipefs_sb = rpc_get_sb_net(clnt->cl_xprt->xprt_net); if (!pipefs_sb) return 0; - path.dentry = rpc_setup_pipedir_sb(pipefs_sb, clnt, dir_name); + dentry = rpc_setup_pipedir_sb(pipefs_sb, clnt, dir_name); rpc_put_sb_net(clnt->cl_xprt->xprt_net); - if (IS_ERR(path.dentry)) - return PTR_ERR(path.dentry); - clnt->cl_path = path; + if (IS_ERR(dentry)) + return PTR_ERR(dentry); + clnt->cl_dentry = dentry; return 0; } @@ -186,7 +185,7 @@ static int __rpc_pipefs_event(struct rpc_clnt *clnt, unsigned long event, BUG_ON(dentry == NULL); if (IS_ERR(dentry)) return PTR_ERR(dentry); - clnt->cl_path.dentry = dentry; + clnt->cl_dentry = dentry; if (clnt->cl_auth->au_ops->pipes_create) { err = clnt->cl_auth->au_ops->pipes_create(clnt->cl_auth); if (err) -- cgit v1.2.3 From 820f9442e711a81749e70c40f149fc54c4ce0ca8 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Fri, 25 Nov 2011 17:12:40 +0300 Subject: SUNRPC: split cache creation and PipeFS registration This precursor patch splits SUNRPC cache creation and PipeFS registartion. It's required for latter split of NFS DNS resolver cache creation per network namespace context and PipeFS registration/unregistration on MOUNT/UMOUNT events. Signed-off-by: Stanislav Kinsbursky Signed-off-by: Trond Myklebust --- fs/nfs/cache_lib.c | 3 +++ include/linux/sunrpc/cache.h | 2 ++ net/sunrpc/cache.c | 12 +++++------- 3 files changed, 10 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/fs/nfs/cache_lib.c b/fs/nfs/cache_lib.c index c98b439332fc..d62a8951cb12 100644 --- a/fs/nfs/cache_lib.c +++ b/fs/nfs/cache_lib.c @@ -120,6 +120,7 @@ int nfs_cache_register(struct cache_detail *cd) mnt = rpc_get_mount(); if (IS_ERR(mnt)) return PTR_ERR(mnt); + sunrpc_init_cache_detail(cd); ret = vfs_path_lookup(mnt->mnt_root, mnt, "/cache", 0, &path); if (ret) goto err; @@ -128,6 +129,7 @@ int nfs_cache_register(struct cache_detail *cd) if (!ret) return ret; err: + sunrpc_destroy_cache_detail(cd); rpc_put_mount(); return ret; } @@ -135,6 +137,7 @@ err: void nfs_cache_unregister(struct cache_detail *cd) { sunrpc_cache_unregister_pipefs(cd); + sunrpc_destroy_cache_detail(cd); rpc_put_mount(); } diff --git a/include/linux/sunrpc/cache.h b/include/linux/sunrpc/cache.h index 57531f8e5956..590a8ab0cec3 100644 --- a/include/linux/sunrpc/cache.h +++ b/include/linux/sunrpc/cache.h @@ -202,6 +202,8 @@ extern int cache_register_net(struct cache_detail *cd, struct net *net); extern void cache_unregister(struct cache_detail *cd); extern void cache_unregister_net(struct cache_detail *cd, struct net *net); +extern void sunrpc_init_cache_detail(struct cache_detail *cd); +extern void sunrpc_destroy_cache_detail(struct cache_detail *cd); extern int sunrpc_cache_register_pipefs(struct dentry *parent, const char *, umode_t, struct cache_detail *); extern void sunrpc_cache_unregister_pipefs(struct cache_detail *); diff --git a/net/sunrpc/cache.c b/net/sunrpc/cache.c index 465df9ae1046..fefe06729f9d 100644 --- a/net/sunrpc/cache.c +++ b/net/sunrpc/cache.c @@ -344,7 +344,7 @@ static int current_index; static void do_cache_clean(struct work_struct *work); static struct delayed_work cache_cleaner; -static void sunrpc_init_cache_detail(struct cache_detail *cd) +void sunrpc_init_cache_detail(struct cache_detail *cd) { rwlock_init(&cd->hash_lock); INIT_LIST_HEAD(&cd->queue); @@ -360,8 +360,9 @@ static void sunrpc_init_cache_detail(struct cache_detail *cd) /* start the cleaning process */ schedule_delayed_work(&cache_cleaner, 0); } +EXPORT_SYMBOL_GPL(sunrpc_init_cache_detail); -static void sunrpc_destroy_cache_detail(struct cache_detail *cd) +void sunrpc_destroy_cache_detail(struct cache_detail *cd) { cache_purge(cd); spin_lock(&cache_list_lock); @@ -384,6 +385,7 @@ static void sunrpc_destroy_cache_detail(struct cache_detail *cd) out: printk(KERN_ERR "nfsd: failed to unregister %s cache\n", cd->name); } +EXPORT_SYMBOL_GPL(sunrpc_destroy_cache_detail); /* clean cache tries to find something to clean * and cleans it. @@ -1787,17 +1789,14 @@ int sunrpc_cache_register_pipefs(struct dentry *parent, struct dentry *dir; int ret = 0; - sunrpc_init_cache_detail(cd); q.name = name; q.len = strlen(name); q.hash = full_name_hash(q.name, q.len); dir = rpc_create_cache_dir(parent, &q, umode, cd); if (!IS_ERR(dir)) cd->u.pipefs.dir = dir; - else { - sunrpc_destroy_cache_detail(cd); + else ret = PTR_ERR(dir); - } return ret; } EXPORT_SYMBOL_GPL(sunrpc_cache_register_pipefs); @@ -1806,7 +1805,6 @@ void sunrpc_cache_unregister_pipefs(struct cache_detail *cd) { rpc_remove_cache_dir(cd->u.pipefs.dir); cd->u.pipefs.dir = NULL; - sunrpc_destroy_cache_detail(cd); } EXPORT_SYMBOL_GPL(sunrpc_cache_unregister_pipefs); -- cgit v1.2.3 From e50a7a1a42335243c94eeea4a8d23413cb02370d Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Tue, 10 Jan 2012 16:12:46 +0400 Subject: NFS: make NFS client allocated per network namespace context This patch adds new net variable to nfs_client structure. This variable is set on NFS client creation and cheched during matching NFS client search. Initially current->nsproxy->net_ns is used as network namespace owner for new NFS client to create. This network namespace pointer is set during mount options parsing and thus can be passed from user-spave utils in future if will be necessary. Signed-off-by: Stanislav Kinsbursky Signed-off-by: Trond Myklebust --- fs/nfs/client.c | 16 +++++++++++++--- fs/nfs/internal.h | 1 + fs/nfs/super.c | 3 +++ include/linux/nfs_fs_sb.h | 1 + 4 files changed, 18 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/fs/nfs/client.c b/fs/nfs/client.c index 31778f74357d..ca016fe44602 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -135,6 +135,7 @@ struct nfs_client_initdata { const struct nfs_rpc_ops *rpc_ops; int proto; u32 minorversion; + struct net *net; }; /* @@ -189,6 +190,7 @@ static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_ if (!IS_ERR(cred)) clp->cl_machine_cred = cred; nfs_fscache_get_client_cookie(clp); + clp->net = cl_init->net; return clp; @@ -481,6 +483,9 @@ static struct nfs_client *nfs_match_client(const struct nfs_client_initdata *dat /* Match the full socket address */ if (!nfs_sockaddr_cmp(sap, clap)) continue; + /* Match network namespace */ + if (clp->net != data->net) + continue; atomic_inc(&clp->cl_count); return clp; @@ -831,6 +836,7 @@ static int nfs_init_server(struct nfs_server *server, .addrlen = data->nfs_server.addrlen, .rpc_ops = &nfs_v2_clientops, .proto = data->nfs_server.protocol, + .net = data->net, }; struct rpc_timeout timeparms; struct nfs_client *clp; @@ -1393,7 +1399,7 @@ static int nfs4_set_client(struct nfs_server *server, const char *ip_addr, rpc_authflavor_t authflavour, int proto, const struct rpc_timeout *timeparms, - u32 minorversion) + u32 minorversion, struct net *net) { struct nfs_client_initdata cl_init = { .hostname = hostname, @@ -1402,6 +1408,7 @@ static int nfs4_set_client(struct nfs_server *server, .rpc_ops = &nfs_v4_clientops, .proto = proto, .minorversion = minorversion, + .net = net, }; struct nfs_client *clp; int error; @@ -1453,6 +1460,7 @@ struct nfs_client *nfs4_set_ds_client(struct nfs_client* mds_clp, .rpc_ops = &nfs_v4_clientops, .proto = ds_proto, .minorversion = mds_clp->cl_minorversion, + .net = mds_clp->net, }; struct rpc_timeout ds_timeout = { .to_initval = 15 * HZ, @@ -1580,7 +1588,8 @@ static int nfs4_init_server(struct nfs_server *server, data->auth_flavors[0], data->nfs_server.protocol, &timeparms, - data->minorversion); + data->minorversion, + data->net); if (error < 0) goto error; @@ -1677,7 +1686,8 @@ struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data, data->authflavor, parent_server->client->cl_xprt->prot, parent_server->client->cl_timeout, - parent_client->cl_mvops->minor_version); + parent_client->cl_mvops->minor_version, + parent_client->net); if (error < 0) goto error; diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index 8102db9b926c..02fb2001a283 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -123,6 +123,7 @@ struct nfs_parsed_mount_data { } nfs_server; struct security_mnt_opts lsm_opts; + struct net *net; }; /* mount_clnt.c */ diff --git a/fs/nfs/super.c b/fs/nfs/super.c index 3dfa4f112c0a..73aa75649bf8 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -52,6 +52,7 @@ #include #include #include +#include #include #include @@ -1107,6 +1108,8 @@ static int nfs_parse_mount_options(char *raw, free_secdata(secdata); + mnt->net = current->nsproxy->net_ns; + while ((p = strsep(&raw, ",")) != NULL) { substring_t args[MAX_OPT_ARGS]; unsigned long option; diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h index ba4d7656ecfd..e0863d35f753 100644 --- a/include/linux/nfs_fs_sb.h +++ b/include/linux/nfs_fs_sb.h @@ -85,6 +85,7 @@ struct nfs_client { #endif struct server_scope *server_scope; /* from exchange_id */ + struct net *net; }; /* -- cgit v1.2.3 From eee17325f1dfbe004f1475743bab9e3d050d00f5 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Tue, 10 Jan 2012 16:13:19 +0400 Subject: NFS: idmap PipeFS notifier introduced v2: 1) Added "nfs_idmap_init" and "nfs_idmap_quit" definitions for kernels built without CONFIG_NFS_V4 option set. This patch subscribes NFS clients to RPC pipefs notifications. Idmap notifier is registering on NFS module load. This notifier callback is responsible for creation/destruction of PipeFS idmap pipe dentry for NFS4 clients. Since ipdmap pipe is created in rpc client pipefs directory, we have make sure, that this directory has been created already. IOW RPC client notifier callback has been called already. To achive this, PipeFS notifier priorities has been introduced (RPC clients notifier priority is greater than NFS idmap one). But this approach gives another problem: unlink for RPC client directory will be called before NFS idmap pipe unlink on UMOUNT event and will fail, because directory is not empty. The solution, introduced in this patch, is to try to remove client directory once again after idmap pipe was unlinked. This looks like ugly hack, so probably it should be replaced in some more elegant way. Note that no locking required in notifier callback because PipeFS superblock pointer is passed as an argument from it's creation or destruction routine and thus we can be sure about it's validity. Signed-off-by: Stanislav Kinsbursky Signed-off-by: Trond Myklebust --- fs/nfs/client.c | 4 +- fs/nfs/idmap.c | 75 ++++++++++++++++++++++++++++++++++++++ fs/nfs/internal.h | 4 ++ include/linux/nfs_idmap.h | 21 ++++++----- include/linux/sunrpc/rpc_pipe_fs.h | 7 ++++ net/sunrpc/clnt.c | 1 + net/sunrpc/rpc_pipe.c | 16 ++++++++ 7 files changed, 116 insertions(+), 12 deletions(-) (limited to 'include/linux') diff --git a/fs/nfs/client.c b/fs/nfs/client.c index 64815b725409..ca9a4aa38dff 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -52,8 +52,8 @@ #define NFSDBG_FACILITY NFSDBG_CLIENT -static DEFINE_SPINLOCK(nfs_client_lock); -static LIST_HEAD(nfs_client_list); +DEFINE_SPINLOCK(nfs_client_lock); +LIST_HEAD(nfs_client_list); static LIST_HEAD(nfs_volume_list); static DECLARE_WAIT_QUEUE_HEAD(nfs_client_active_wq); #ifdef CONFIG_NFS_V4 diff --git a/fs/nfs/idmap.c b/fs/nfs/idmap.c index 769274ed51c4..ff084d258c41 100644 --- a/fs/nfs/idmap.c +++ b/fs/nfs/idmap.c @@ -377,6 +377,7 @@ int nfs_map_gid_to_group(const struct nfs_server *server, __u32 gid, char *buf, #include #include "nfs4_fs.h" +#include "internal.h" #define IDMAP_HASH_SZ 128 @@ -530,6 +531,80 @@ nfs_idmap_delete(struct nfs_client *clp) kfree(idmap); } +static int __rpc_pipefs_event(struct nfs_client *clp, unsigned long event, + struct super_block *sb) +{ + int err = 0; + + switch (event) { + case RPC_PIPEFS_MOUNT: + BUG_ON(clp->cl_rpcclient->cl_dentry == NULL); + err = __nfs_idmap_register(clp->cl_rpcclient->cl_dentry, + clp->cl_idmap, + clp->cl_idmap->idmap_pipe); + break; + case RPC_PIPEFS_UMOUNT: + if (clp->cl_idmap->idmap_pipe) { + struct dentry *parent; + + parent = clp->cl_idmap->idmap_pipe->dentry->d_parent; + __nfs_idmap_unregister(clp->cl_idmap->idmap_pipe); + /* + * Note: This is a dirty hack. SUNRPC hook has been + * called already but simple_rmdir() call for the + * directory returned with error because of idmap pipe + * inside. Thus now we have to remove this directory + * here. + */ + if (rpc_rmdir(parent)) + printk(KERN_ERR "%s: failed to remove clnt dir!\n", __func__); + } + break; + default: + printk(KERN_ERR "%s: unknown event: %ld\n", __func__, event); + return -ENOTSUPP; + } + return err; +} + +static int rpc_pipefs_event(struct notifier_block *nb, unsigned long event, + void *ptr) +{ + struct super_block *sb = ptr; + struct nfs_client *clp; + int error = 0; + + spin_lock(&nfs_client_lock); + list_for_each_entry(clp, &nfs_client_list, cl_share_link) { + if (clp->net != sb->s_fs_info) + continue; + if (clp->rpc_ops != &nfs_v4_clientops) + continue; + error = __rpc_pipefs_event(clp, event, sb); + if (error) + break; + } + spin_unlock(&nfs_client_lock); + return error; +} + +#define PIPEFS_NFS_PRIO 1 + +static struct notifier_block nfs_idmap_block = { + .notifier_call = rpc_pipefs_event, + .priority = SUNRPC_PIPEFS_NFS_PRIO, +}; + +int nfs_idmap_init(void) +{ + return rpc_pipefs_notifier_register(&nfs_idmap_block); +} + +void nfs_idmap_quit(void) +{ + rpc_pipefs_notifier_unregister(&nfs_idmap_block); +} + /* * Helper routines for manipulating the hashtable */ diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index d602188f889f..2b9836fe4434 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -182,6 +182,10 @@ static inline void nfs_fs_proc_exit(void) { } #endif +#ifdef CONFIG_NFS_V4 +extern spinlock_t nfs_client_lock; +extern struct list_head nfs_client_list; +#endif /* nfs4namespace.c */ #ifdef CONFIG_NFS_V4 diff --git a/include/linux/nfs_idmap.h b/include/linux/nfs_idmap.h index 308c18877018..3c9eeb7da646 100644 --- a/include/linux/nfs_idmap.h +++ b/include/linux/nfs_idmap.h @@ -69,31 +69,32 @@ struct nfs_server; struct nfs_fattr; struct nfs4_string; -#ifdef CONFIG_NFS_USE_NEW_IDMAPPER - +#ifdef CONFIG_NFS_V4 int nfs_idmap_init(void); void nfs_idmap_quit(void); - -static inline int nfs_idmap_new(struct nfs_client *clp) +#else +static inline int nfs_idmap_init(void) { return 0; } -static inline void nfs_idmap_delete(struct nfs_client *clp) -{ -} +static inline void nfs_idmap_quit(void) +{} +#endif -#else /* CONFIG_NFS_USE_NEW_IDMAPPER not set */ +#ifdef CONFIG_NFS_USE_NEW_IDMAPPER -static inline int nfs_idmap_init(void) +static inline int nfs_idmap_new(struct nfs_client *clp) { return 0; } -static inline void nfs_idmap_quit(void) +static inline void nfs_idmap_delete(struct nfs_client *clp) { } +#else /* CONFIG_NFS_USE_NEW_IDMAPPER not set */ + int nfs_idmap_new(struct nfs_client *); void nfs_idmap_delete(struct nfs_client *); diff --git a/include/linux/sunrpc/rpc_pipe_fs.h b/include/linux/sunrpc/rpc_pipe_fs.h index 0d1f748f76da..ca32ebd14c18 100644 --- a/include/linux/sunrpc/rpc_pipe_fs.h +++ b/include/linux/sunrpc/rpc_pipe_fs.h @@ -49,6 +49,11 @@ RPC_I(struct inode *inode) return container_of(inode, struct rpc_inode, vfs_inode); } +enum { + SUNRPC_PIPEFS_NFS_PRIO, + SUNRPC_PIPEFS_RPC_PRIO, +}; + extern int rpc_pipefs_notifier_register(struct notifier_block *); extern void rpc_pipefs_notifier_unregister(struct notifier_block *); @@ -78,6 +83,8 @@ extern struct dentry *rpc_create_cache_dir(struct dentry *, struct cache_detail *); extern void rpc_remove_cache_dir(struct dentry *); +extern int rpc_rmdir(struct dentry *dentry); + struct rpc_pipe *rpc_mkpipe_data(const struct rpc_pipe_ops *ops, int flags); void rpc_destroy_pipe_data(struct rpc_pipe *pipe); extern struct dentry *rpc_mkpipe_dentry(struct dentry *, const char *, void *, diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index ed7c22de9319..4c6848017168 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -222,6 +222,7 @@ static int rpc_pipefs_event(struct notifier_block *nb, unsigned long event, static struct notifier_block rpc_clients_block = { .notifier_call = rpc_pipefs_event, + .priority = SUNRPC_PIPEFS_RPC_PRIO, }; int rpc_clients_notifier_register(void) diff --git a/net/sunrpc/rpc_pipe.c b/net/sunrpc/rpc_pipe.c index 6b417fcabdbf..bae4e71d8663 100644 --- a/net/sunrpc/rpc_pipe.c +++ b/net/sunrpc/rpc_pipe.c @@ -616,6 +616,22 @@ static int __rpc_rmdir(struct inode *dir, struct dentry *dentry) return ret; } +int rpc_rmdir(struct dentry *dentry) +{ + struct dentry *parent; + struct inode *dir; + int error; + + parent = dget_parent(dentry); + dir = parent->d_inode; + mutex_lock_nested(&dir->i_mutex, I_MUTEX_PARENT); + error = __rpc_rmdir(dir, dentry); + mutex_unlock(&dir->i_mutex); + dput(parent); + return error; +} +EXPORT_SYMBOL_GPL(rpc_rmdir); + static int __rpc_unlink(struct inode *dir, struct dentry *dentry) { int ret; -- cgit v1.2.3 From 12bc372b96b35a2dc9245ec61369028932b82ea8 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Tue, 10 Jan 2012 17:04:48 +0400 Subject: SUNRPC: kernel PipeFS mount point creation routines removed This patch removes static rpc_mnt variable and its creation and destruction routines, because they are not used anymore. Signed-off-by: Stanislav Kinsbursky Signed-off-by: Trond Myklebust --- include/linux/sunrpc/rpc_pipe_fs.h | 2 -- net/sunrpc/rpc_pipe.c | 21 --------------------- 2 files changed, 23 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sunrpc/rpc_pipe_fs.h b/include/linux/sunrpc/rpc_pipe_fs.h index ca32ebd14c18..426ce6eeee66 100644 --- a/include/linux/sunrpc/rpc_pipe_fs.h +++ b/include/linux/sunrpc/rpc_pipe_fs.h @@ -90,8 +90,6 @@ void rpc_destroy_pipe_data(struct rpc_pipe *pipe); extern struct dentry *rpc_mkpipe_dentry(struct dentry *, const char *, void *, struct rpc_pipe *); extern int rpc_unlink(struct dentry *); -extern struct vfsmount *rpc_get_mount(void); -extern void rpc_put_mount(void); extern int register_rpc_pipefs(void); extern void unregister_rpc_pipefs(void); diff --git a/net/sunrpc/rpc_pipe.c b/net/sunrpc/rpc_pipe.c index bae4e71d8663..6873c9b51cc9 100644 --- a/net/sunrpc/rpc_pipe.c +++ b/net/sunrpc/rpc_pipe.c @@ -18,7 +18,6 @@ #include #include -#include #include #include #include @@ -37,9 +36,6 @@ #define NET_NAME(net) ((net == &init_net) ? " (init_net)" : "") -static struct vfsmount *rpc_mnt __read_mostly; -static int rpc_mount_count; - static struct file_system_type rpc_pipe_fs_type; @@ -449,23 +445,6 @@ struct rpc_filelist { umode_t mode; }; -struct vfsmount *rpc_get_mount(void) -{ - int err; - - err = simple_pin_fs(&rpc_pipe_fs_type, &rpc_mnt, &rpc_mount_count); - if (err != 0) - return ERR_PTR(err); - return rpc_mnt; -} -EXPORT_SYMBOL_GPL(rpc_get_mount); - -void rpc_put_mount(void) -{ - simple_release_fs(&rpc_mnt, &rpc_mount_count); -} -EXPORT_SYMBOL_GPL(rpc_put_mount); - static int rpc_delete_dentry(const struct dentry *dentry) { return 1; -- cgit v1.2.3 From 1313e6034a73a55d6293dbdc62b8853dd067771a Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Tue, 17 Jan 2012 22:04:24 -0500 Subject: NFS: Remove unnecessary includes from linux/nfs_fs_i.h Also from linux/nfs_xdr.h. Signed-off-by: Trond Myklebust --- fs/nfs/pagelist.c | 1 + include/linux/nfs_fs_i.h | 4 ---- include/linux/nfs_xdr.h | 1 - 3 files changed, 1 insertion(+), 5 deletions(-) (limited to 'include/linux') diff --git a/fs/nfs/pagelist.c b/fs/nfs/pagelist.c index 5668f7c54c41..77a184e2fe47 100644 --- a/fs/nfs/pagelist.c +++ b/fs/nfs/pagelist.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include diff --git a/include/linux/nfs_fs_i.h b/include/linux/nfs_fs_i.h index 861730275ba0..a5c50d97341e 100644 --- a/include/linux/nfs_fs_i.h +++ b/include/linux/nfs_fs_i.h @@ -1,10 +1,6 @@ #ifndef _NFS_FS_I #define _NFS_FS_I -#include -#include -#include - struct nlm_lockowner; /* diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index a764cef06b73..f5188e10ea0e 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -2,7 +2,6 @@ #define _LINUX_NFS_XDR_H #include -#include #include /* -- cgit v1.2.3 From 9157c31dd610a127bc6f01bc1953cf8b80382040 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Tue, 17 Jan 2012 22:04:24 -0500 Subject: NFSv4: Replace state_owner->so_owner_id with an ida based allocator We're unlikely to ever need more than 2^31 simultaneous open owners, so let's replace the custom allocator with the generic ida allocator. Signed-off-by: Trond Myklebust --- fs/nfs/client.c | 2 ++ fs/nfs/nfs4_fs.h | 2 +- fs/nfs/nfs4proc.c | 4 ++-- fs/nfs/nfs4state.c | 17 ++++++++++++----- include/linux/nfs_fs_sb.h | 3 ++- 5 files changed, 19 insertions(+), 9 deletions(-) (limited to 'include/linux') diff --git a/fs/nfs/client.c b/fs/nfs/client.c index ca9a4aa38dff..8d1739d3424d 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -1092,6 +1092,7 @@ static struct nfs_server *nfs_alloc_server(void) return NULL; } + ida_init(&server->openowner_id); pnfs_init_server(server); return server; @@ -1117,6 +1118,7 @@ void nfs_free_server(struct nfs_server *server) nfs_put_client(server->nfs_client); + ida_destroy(&server->openowner_id); nfs_free_iostats(server->io_stats); bdi_destroy(&server->backing_dev_info); kfree(server); diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index 654b091644c5..091b679747ed 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -92,7 +92,6 @@ struct nfs_unique_id { * semantics by allowing the server to identify replayed requests. */ struct nfs4_state_owner { - struct nfs_unique_id so_owner_id; struct nfs_server *so_server; struct list_head so_lru; unsigned long so_expires; @@ -106,6 +105,7 @@ struct nfs4_state_owner { struct list_head so_states; struct nfs_seqid_counter so_seqid; struct rpc_sequence so_sequence; + int so_owner_id; }; enum { diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 53ef365f4372..1dd2e407a901 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -815,7 +815,7 @@ static struct nfs4_opendata *nfs4_opendata_alloc(struct dentry *dentry, p->o_arg.open_flags = flags; p->o_arg.fmode = fmode & (FMODE_READ|FMODE_WRITE); p->o_arg.clientid = server->nfs_client->cl_clientid; - p->o_arg.id = sp->so_owner_id.id; + p->o_arg.id = sp->so_owner_id; p->o_arg.name = &dentry->d_name; p->o_arg.server = server; p->o_arg.bitmask = server->attr_bitmask; @@ -1440,7 +1440,7 @@ static void nfs4_open_prepare(struct rpc_task *task, void *calldata) rcu_read_unlock(); } /* Update sequence id. */ - data->o_arg.id = sp->so_owner_id.id; + data->o_arg.id = sp->so_owner_id; data->o_arg.clientid = sp->so_server->nfs_client->cl_clientid; if (data->o_arg.claim == NFS4_OPEN_CLAIM_PREVIOUS) { task->tk_msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_NOATTR]; diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index a8a42a677d8f..8472707286f9 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -405,6 +405,7 @@ nfs4_insert_state_owner_locked(struct nfs4_state_owner *new) struct rb_node **p = &server->state_owners.rb_node, *parent = NULL; struct nfs4_state_owner *sp; + int err; while (*p != NULL) { parent = *p; @@ -421,8 +422,9 @@ nfs4_insert_state_owner_locked(struct nfs4_state_owner *new) return sp; } } - nfs_alloc_unique_id_locked(&server->openowner_id, - &new->so_owner_id, 1, 64); + err = ida_get_new(&server->openowner_id, &new->so_owner_id); + if (err) + return ERR_PTR(err); rb_link_node(&new->so_server_node, parent, p); rb_insert_color(&new->so_server_node, &server->state_owners); return new; @@ -435,7 +437,7 @@ nfs4_remove_state_owner_locked(struct nfs4_state_owner *sp) if (!RB_EMPTY_NODE(&sp->so_server_node)) rb_erase(&sp->so_server_node, &server->state_owners); - nfs_free_unique_id(&server->openowner_id, &sp->so_owner_id); + ida_remove(&server->openowner_id, sp->so_owner_id); } /* @@ -534,8 +536,13 @@ struct nfs4_state_owner *nfs4_get_state_owner(struct nfs_server *server, new = nfs4_alloc_state_owner(server, cred, gfp_flags); if (new == NULL) goto out; - sp = nfs4_insert_state_owner_locked(new); - spin_unlock(&clp->cl_lock); + do { + if (ida_pre_get(&server->openowner_id, gfp_flags) == 0) + break; + spin_lock(&clp->cl_lock); + sp = nfs4_insert_state_owner_locked(new); + spin_unlock(&clp->cl_lock); + } while (sp == ERR_PTR(-EAGAIN)); if (sp != new) nfs4_free_state_owner(new); out: diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h index e0863d35f753..645537e18bd0 100644 --- a/include/linux/nfs_fs_sb.h +++ b/include/linux/nfs_fs_sb.h @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -151,9 +152,9 @@ struct nfs_server { /* the following fields are protected by nfs_client->cl_lock */ struct rb_root state_owners; - struct rb_root openowner_id; struct rb_root lockowner_id; #endif + struct ida openowner_id; struct list_head state_owners_lru; struct list_head layouts; struct list_head delegations; -- cgit v1.2.3 From d2d7ce28a2f8ec6ca2a49145e643d2e3c7d21ba3 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Tue, 17 Jan 2012 22:04:25 -0500 Subject: NFSv4: Replace lock_owner->ld_id with an ida based allocator Again, We're unlikely to ever need more than 2^31 simultaneous lock owners, so let's replace the custom allocator. Now that there are no more users, we can also get rid of the custom allocator code. Signed-off-by: Trond Myklebust --- fs/nfs/client.c | 2 ++ fs/nfs/nfs4_fs.h | 7 +---- fs/nfs/nfs4proc.c | 6 ++-- fs/nfs/nfs4state.c | 74 +++++------------------------------------------ include/linux/nfs_fs_sb.h | 2 +- 5 files changed, 15 insertions(+), 76 deletions(-) (limited to 'include/linux') diff --git a/fs/nfs/client.c b/fs/nfs/client.c index 8d1739d3424d..df60d9971b95 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -1093,6 +1093,7 @@ static struct nfs_server *nfs_alloc_server(void) } ida_init(&server->openowner_id); + ida_init(&server->lockowner_id); pnfs_init_server(server); return server; @@ -1118,6 +1119,7 @@ void nfs_free_server(struct nfs_server *server) nfs_put_client(server->nfs_client); + ida_destroy(&server->lockowner_id); ida_destroy(&server->openowner_id); nfs_free_iostats(server->io_stats); bdi_destroy(&server->backing_dev_info); diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index 091b679747ed..b23cb0cda632 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -81,11 +81,6 @@ static inline void nfs_confirm_seqid(struct nfs_seqid_counter *seqid, int status seqid->flags |= NFS_SEQID_CONFIRMED; } -struct nfs_unique_id { - struct rb_node rb_node; - __u64 id; -}; - /* * NFS4 state_owners and lock_owners are simply labels for ordered * sequences of RPC calls. Their sole purpose is to provide once-only @@ -145,9 +140,9 @@ struct nfs4_lock_state { struct nfs4_state * ls_state; /* Pointer to open state */ #define NFS_LOCK_INITIALIZED 1 int ls_flags; + int ls_id; struct nfs_seqid_counter ls_seqid; struct rpc_sequence ls_sequence; - struct nfs_unique_id ls_id; nfs4_stateid ls_stateid; atomic_t ls_count; struct nfs4_lock_owner ls_owner; diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 1dd2e407a901..9c77af900960 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -4017,7 +4017,7 @@ static int _nfs4_proc_getlk(struct nfs4_state *state, int cmd, struct file_lock if (status != 0) goto out; lsp = request->fl_u.nfs4_fl.owner; - arg.lock_owner.id = lsp->ls_id.id; + arg.lock_owner.id = lsp->ls_id; arg.lock_owner.s_dev = server->s_dev; status = nfs4_call_sync(server->client, server, &msg, &arg.seq_args, &res.seq_res, 1); switch (status) { @@ -4262,7 +4262,7 @@ static struct nfs4_lockdata *nfs4_alloc_lockdata(struct file_lock *fl, goto out_free_seqid; p->arg.lock_stateid = &lsp->ls_stateid; p->arg.lock_owner.clientid = server->nfs_client->cl_clientid; - p->arg.lock_owner.id = lsp->ls_id.id; + p->arg.lock_owner.id = lsp->ls_id; p->arg.lock_owner.s_dev = server->s_dev; p->res.lock_seqid = p->arg.lock_seqid; p->lsp = lsp; @@ -4679,7 +4679,7 @@ void nfs4_release_lockowner(const struct nfs4_lock_state *lsp) if (!args) return; args->lock_owner.clientid = server->nfs_client->cl_clientid; - args->lock_owner.id = lsp->ls_id.id; + args->lock_owner.id = lsp->ls_id; args->lock_owner.s_dev = server->s_dev; msg.rpc_argp = args; rpc_call_async(server->client, &msg, 0, &nfs4_release_lockowner_ops, args); diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 8472707286f9..5abf23615bc5 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -317,62 +317,6 @@ out: return cred; } -static void nfs_alloc_unique_id_locked(struct rb_root *root, - struct nfs_unique_id *new, - __u64 minval, int maxbits) -{ - struct rb_node **p, *parent; - struct nfs_unique_id *pos; - __u64 mask = ~0ULL; - - if (maxbits < 64) - mask = (1ULL << maxbits) - 1ULL; - - /* Ensure distribution is more or less flat */ - get_random_bytes(&new->id, sizeof(new->id)); - new->id &= mask; - if (new->id < minval) - new->id += minval; -retry: - p = &root->rb_node; - parent = NULL; - - while (*p != NULL) { - parent = *p; - pos = rb_entry(parent, struct nfs_unique_id, rb_node); - - if (new->id < pos->id) - p = &(*p)->rb_left; - else if (new->id > pos->id) - p = &(*p)->rb_right; - else - goto id_exists; - } - rb_link_node(&new->rb_node, parent, p); - rb_insert_color(&new->rb_node, root); - return; -id_exists: - for (;;) { - new->id++; - if (new->id < minval || (new->id & mask) != new->id) { - new->id = minval; - break; - } - parent = rb_next(parent); - if (parent == NULL) - break; - pos = rb_entry(parent, struct nfs_unique_id, rb_node); - if (new->id < pos->id) - break; - } - goto retry; -} - -static void nfs_free_unique_id(struct rb_root *root, struct nfs_unique_id *id) -{ - rb_erase(&id->rb_node, root); -} - static struct nfs4_state_owner * nfs4_find_state_owner_locked(struct nfs_server *server, struct rpc_cred *cred) { @@ -800,7 +744,6 @@ static struct nfs4_lock_state *nfs4_alloc_lock_state(struct nfs4_state *state, f { struct nfs4_lock_state *lsp; struct nfs_server *server = state->owner->so_server; - struct nfs_client *clp = server->nfs_client; lsp = kzalloc(sizeof(*lsp), GFP_NOFS); if (lsp == NULL) @@ -820,24 +763,23 @@ static struct nfs4_lock_state *nfs4_alloc_lock_state(struct nfs4_state *state, f lsp->ls_owner.lo_u.posix_owner = fl_owner; break; default: - kfree(lsp); - return NULL; + goto out_free; } - spin_lock(&clp->cl_lock); - nfs_alloc_unique_id_locked(&server->lockowner_id, &lsp->ls_id, 1, 64); - spin_unlock(&clp->cl_lock); + lsp->ls_id = ida_simple_get(&server->lockowner_id, 0, 0, GFP_NOFS); + if (lsp->ls_id < 0) + goto out_free; INIT_LIST_HEAD(&lsp->ls_locks); return lsp; +out_free: + kfree(lsp); + return NULL; } static void nfs4_free_lock_state(struct nfs4_lock_state *lsp) { struct nfs_server *server = lsp->ls_state->owner->so_server; - struct nfs_client *clp = server->nfs_client; - spin_lock(&clp->cl_lock); - nfs_free_unique_id(&server->lockowner_id, &lsp->ls_id); - spin_unlock(&clp->cl_lock); + ida_simple_remove(&server->lockowner_id, lsp->ls_id); rpc_destroy_wait_queue(&lsp->ls_sequence.wait); kfree(lsp); } diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h index 645537e18bd0..c23469308b8e 100644 --- a/include/linux/nfs_fs_sb.h +++ b/include/linux/nfs_fs_sb.h @@ -152,9 +152,9 @@ struct nfs_server { /* the following fields are protected by nfs_client->cl_lock */ struct rb_root state_owners; - struct rb_root lockowner_id; #endif struct ida openowner_id; + struct ida lockowner_id; struct list_head state_owners_lru; struct list_head layouts; struct list_head delegations; -- cgit v1.2.3 From 961a828df64979d2a9faeeeee043391670a193b9 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Tue, 17 Jan 2012 22:57:37 -0500 Subject: SUNRPC: Fix potential races in xprt_lock_write_next() We have to ensure that the wake up from the waitqueue and the assignment of xprt->snd_task are atomic. We can do this by assigning the snd_task while under the waitqueue spinlock. Signed-off-by: Trond Myklebust --- fs/nfs/nfs4_fs.h | 1 + fs/nfs/nfs4proc.c | 13 +++++++----- fs/nfs/nfs4state.c | 17 ++++++++------- include/linux/sunrpc/sched.h | 3 +++ net/sunrpc/sched.c | 42 +++++++++++++++++++++++++++++-------- net/sunrpc/xprt.c | 49 +++++++++++++++++++++++--------------------- 6 files changed, 79 insertions(+), 46 deletions(-) (limited to 'include/linux') diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index df3d02c3e8cb..c45c21a5470f 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -222,6 +222,7 @@ static inline struct nfs4_session *nfs4_get_session(const struct nfs_server *ser return server->nfs_client->cl_session; } +extern bool nfs4_set_task_privileged(struct rpc_task *task, void *dummy); extern int nfs4_setup_sequence(const struct nfs_server *server, struct nfs4_sequence_args *args, struct nfs4_sequence_res *res, struct rpc_task *task); diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 360240cc1e9b..828a76590af9 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -385,17 +385,20 @@ nfs4_free_slot(struct nfs4_slot_table *tbl, u8 free_slotid) free_slotid, tbl->highest_used_slotid); } +bool nfs4_set_task_privileged(struct rpc_task *task, void *dummy) +{ + rpc_task_set_priority(task, RPC_PRIORITY_PRIVILEGED); + return true; +} + /* * Signal state manager thread if session fore channel is drained */ static void nfs4_check_drain_fc_complete(struct nfs4_session *ses) { - struct rpc_task *task; - if (!test_bit(NFS4_SESSION_DRAINING, &ses->session_state)) { - task = rpc_wake_up_next(&ses->fc_slot_table.slot_tbl_waitq); - if (task) - rpc_task_set_priority(task, RPC_PRIORITY_PRIVILEGED); + rpc_wake_up_first(&ses->fc_slot_table.slot_tbl_waitq, + nfs4_set_task_privileged, NULL); return; } diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index a42e60d3ee50..f0e9881c2aa2 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -190,23 +190,22 @@ static int nfs41_setup_state_renewal(struct nfs_client *clp) static void nfs4_end_drain_session(struct nfs_client *clp) { struct nfs4_session *ses = clp->cl_session; + struct nfs4_slot_table *tbl; int max_slots; if (ses == NULL) return; + tbl = &ses->fc_slot_table; if (test_and_clear_bit(NFS4_SESSION_DRAINING, &ses->session_state)) { - spin_lock(&ses->fc_slot_table.slot_tbl_lock); - max_slots = ses->fc_slot_table.max_slots; + spin_lock(&tbl->slot_tbl_lock); + max_slots = tbl->max_slots; while (max_slots--) { - struct rpc_task *task; - - task = rpc_wake_up_next(&ses->fc_slot_table. - slot_tbl_waitq); - if (!task) + if (rpc_wake_up_first(&tbl->slot_tbl_waitq, + nfs4_set_task_privileged, + NULL) == NULL) break; - rpc_task_set_priority(task, RPC_PRIORITY_PRIVILEGED); } - spin_unlock(&ses->fc_slot_table.slot_tbl_lock); + spin_unlock(&tbl->slot_tbl_lock); } } diff --git a/include/linux/sunrpc/sched.h b/include/linux/sunrpc/sched.h index b16243a35f0b..bd337f990a41 100644 --- a/include/linux/sunrpc/sched.h +++ b/include/linux/sunrpc/sched.h @@ -235,6 +235,9 @@ void rpc_wake_up_queued_task(struct rpc_wait_queue *, struct rpc_task *); void rpc_wake_up(struct rpc_wait_queue *); struct rpc_task *rpc_wake_up_next(struct rpc_wait_queue *); +struct rpc_task *rpc_wake_up_first(struct rpc_wait_queue *, + bool (*)(struct rpc_task *, void *), + void *); void rpc_wake_up_status(struct rpc_wait_queue *, int); int rpc_queue_empty(struct rpc_wait_queue *); void rpc_delay(struct rpc_task *, unsigned long); diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c index 3341d8962786..f982dfe53993 100644 --- a/net/sunrpc/sched.c +++ b/net/sunrpc/sched.c @@ -422,7 +422,7 @@ EXPORT_SYMBOL_GPL(rpc_wake_up_queued_task); /* * Wake up the next task on a priority queue. */ -static struct rpc_task * __rpc_wake_up_next_priority(struct rpc_wait_queue *queue) +static struct rpc_task *__rpc_find_next_queued_priority(struct rpc_wait_queue *queue) { struct list_head *q; struct rpc_task *task; @@ -467,30 +467,54 @@ new_queue: new_owner: rpc_set_waitqueue_owner(queue, task->tk_owner); out: - rpc_wake_up_task_queue_locked(queue, task); return task; } +static struct rpc_task *__rpc_find_next_queued(struct rpc_wait_queue *queue) +{ + if (RPC_IS_PRIORITY(queue)) + return __rpc_find_next_queued_priority(queue); + if (!list_empty(&queue->tasks[0])) + return list_first_entry(&queue->tasks[0], struct rpc_task, u.tk_wait.list); + return NULL; +} + /* - * Wake up the next task on the wait queue. + * Wake up the first task on the wait queue. */ -struct rpc_task * rpc_wake_up_next(struct rpc_wait_queue *queue) +struct rpc_task *rpc_wake_up_first(struct rpc_wait_queue *queue, + bool (*func)(struct rpc_task *, void *), void *data) { struct rpc_task *task = NULL; - dprintk("RPC: wake_up_next(%p \"%s\")\n", + dprintk("RPC: wake_up_first(%p \"%s\")\n", queue, rpc_qname(queue)); spin_lock_bh(&queue->lock); - if (RPC_IS_PRIORITY(queue)) - task = __rpc_wake_up_next_priority(queue); - else { - task_for_first(task, &queue->tasks[0]) + task = __rpc_find_next_queued(queue); + if (task != NULL) { + if (func(task, data)) rpc_wake_up_task_queue_locked(queue, task); + else + task = NULL; } spin_unlock_bh(&queue->lock); return task; } +EXPORT_SYMBOL_GPL(rpc_wake_up_first); + +static bool rpc_wake_up_next_func(struct rpc_task *task, void *data) +{ + return true; +} + +/* + * Wake up the next task on the wait queue. +*/ +struct rpc_task *rpc_wake_up_next(struct rpc_wait_queue *queue) +{ + return rpc_wake_up_first(queue, rpc_wake_up_next_func, NULL); +} EXPORT_SYMBOL_GPL(rpc_wake_up_next); /** diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c index c64c0ef519b5..839f6ef2326b 100644 --- a/net/sunrpc/xprt.c +++ b/net/sunrpc/xprt.c @@ -292,54 +292,57 @@ static inline int xprt_lock_write(struct rpc_xprt *xprt, struct rpc_task *task) return retval; } -static void __xprt_lock_write_next(struct rpc_xprt *xprt) +static bool __xprt_lock_write_func(struct rpc_task *task, void *data) { - struct rpc_task *task; + struct rpc_xprt *xprt = data; struct rpc_rqst *req; - if (test_and_set_bit(XPRT_LOCKED, &xprt->state)) - return; - - task = rpc_wake_up_next(&xprt->sending); - if (task == NULL) - goto out_unlock; - req = task->tk_rqstp; xprt->snd_task = task; if (req) { req->rq_bytes_sent = 0; req->rq_ntrans++; } - return; + return true; +} -out_unlock: +static void __xprt_lock_write_next(struct rpc_xprt *xprt) +{ + if (test_and_set_bit(XPRT_LOCKED, &xprt->state)) + return; + + if (rpc_wake_up_first(&xprt->sending, __xprt_lock_write_func, xprt)) + return; xprt_clear_locked(xprt); } -static void __xprt_lock_write_next_cong(struct rpc_xprt *xprt) +static bool __xprt_lock_write_cong_func(struct rpc_task *task, void *data) { - struct rpc_task *task; + struct rpc_xprt *xprt = data; struct rpc_rqst *req; - if (test_and_set_bit(XPRT_LOCKED, &xprt->state)) - return; - if (RPCXPRT_CONGESTED(xprt)) - goto out_unlock; - task = rpc_wake_up_next(&xprt->sending); - if (task == NULL) - goto out_unlock; - req = task->tk_rqstp; if (req == NULL) { xprt->snd_task = task; - return; + return true; } if (__xprt_get_cong(xprt, task)) { xprt->snd_task = task; req->rq_bytes_sent = 0; req->rq_ntrans++; - return; + return true; } + return false; +} + +static void __xprt_lock_write_next_cong(struct rpc_xprt *xprt) +{ + if (test_and_set_bit(XPRT_LOCKED, &xprt->state)) + return; + if (RPCXPRT_CONGESTED(xprt)) + goto out_unlock; + if (rpc_wake_up_first(&xprt->sending, __xprt_lock_write_cong_func, xprt)) + return; out_unlock: xprt_clear_locked(xprt); } -- cgit v1.2.3 From 977ac3157328239a0f4074b13a3d9eb5c832cd6c Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Fri, 13 Jan 2012 12:52:43 +0400 Subject: SUNRPC: register rpcbind programs in passed network namespase context Registering rpcbind program requires rpcbind clients, which are per network namespace context. Signed-off-by: Stanislav Kinsbursky Signed-off-by: Trond Myklebust --- include/linux/sunrpc/clnt.h | 5 +++-- net/sunrpc/rpcb_clnt.c | 8 ++++---- net/sunrpc/svc.c | 10 +++++----- 3 files changed, 12 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sunrpc/clnt.h b/include/linux/sunrpc/clnt.h index bfd61852b718..b0b3e572a619 100644 --- a/include/linux/sunrpc/clnt.h +++ b/include/linux/sunrpc/clnt.h @@ -138,8 +138,9 @@ void rpc_task_release_client(struct rpc_task *); int rpcb_create_local(void); void rpcb_put_local(void); -int rpcb_register(u32, u32, int, unsigned short); -int rpcb_v4_register(const u32 program, const u32 version, +int rpcb_register(struct net *, u32, u32, int, unsigned short); +int rpcb_v4_register(struct net *net, const u32 program, + const u32 version, const struct sockaddr *address, const char *netid); void rpcb_getport_async(struct rpc_task *); diff --git a/net/sunrpc/rpcb_clnt.c b/net/sunrpc/rpcb_clnt.c index 0d76c0f09bb8..a5aa50d0aae4 100644 --- a/net/sunrpc/rpcb_clnt.c +++ b/net/sunrpc/rpcb_clnt.c @@ -425,7 +425,7 @@ static int rpcb_register_call(struct rpc_clnt *clnt, struct rpc_message *msg) * IN6ADDR_ANY (ie available for all AF_INET and AF_INET6 * addresses). */ -int rpcb_register(u32 prog, u32 vers, int prot, unsigned short port) +int rpcb_register(struct net *net, u32 prog, u32 vers, int prot, unsigned short port) { struct rpcbind_args map = { .r_prog = prog, @@ -436,7 +436,7 @@ int rpcb_register(u32 prog, u32 vers, int prot, unsigned short port) struct rpc_message msg = { .rpc_argp = &map, }; - struct sunrpc_net *sn = net_generic(&init_net, sunrpc_net_id); + struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); dprintk("RPC: %sregistering (%u, %u, %d, %u) with local " "rpcbind\n", (port ? "" : "un"), @@ -563,7 +563,7 @@ static int rpcb_unregister_all_protofamilies(struct sunrpc_net *sn, * service on any IPv4 address, but not on IPv6. The latter * advertises the service on all IPv4 and IPv6 addresses. */ -int rpcb_v4_register(const u32 program, const u32 version, +int rpcb_v4_register(struct net *net, const u32 program, const u32 version, const struct sockaddr *address, const char *netid) { struct rpcbind_args map = { @@ -575,7 +575,7 @@ int rpcb_v4_register(const u32 program, const u32 version, struct rpc_message msg = { .rpc_argp = &map, }; - struct sunrpc_net *sn = net_generic(&init_net, sunrpc_net_id); + struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); if (sn->rpcb_local_clnt4 == NULL) return -EPROTONOSUPPORT; diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index e4aabc02368b..889d7b11b9ac 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -818,7 +818,7 @@ static int __svc_rpcb_register4(const u32 program, const u32 version, return -ENOPROTOOPT; } - error = rpcb_v4_register(program, version, + error = rpcb_v4_register(&init_net, program, version, (const struct sockaddr *)&sin, netid); /* @@ -826,7 +826,7 @@ static int __svc_rpcb_register4(const u32 program, const u32 version, * registration request with the legacy rpcbind v2 protocol. */ if (error == -EPROTONOSUPPORT) - error = rpcb_register(program, version, protocol, port); + error = rpcb_register(&init_net, program, version, protocol, port); return error; } @@ -865,7 +865,7 @@ static int __svc_rpcb_register6(const u32 program, const u32 version, return -ENOPROTOOPT; } - error = rpcb_v4_register(program, version, + error = rpcb_v4_register(&init_net, program, version, (const struct sockaddr *)&sin6, netid); /* @@ -968,14 +968,14 @@ static void __svc_unregister(const u32 program, const u32 version, { int error; - error = rpcb_v4_register(program, version, NULL, ""); + error = rpcb_v4_register(&init_net, program, version, NULL, ""); /* * User space didn't support rpcbind v4, so retry this * request with the legacy rpcbind v2 protocol. */ if (error == -EPROTONOSUPPORT) - error = rpcb_register(program, version, 0, 0); + error = rpcb_register(&init_net, program, version, 0, 0); dprintk("svc: %s(%sv%u), error %d\n", __func__, progname, version, error); -- cgit v1.2.3 From f7a30c18e8d673c996095420a026a28433cb4096 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Fri, 13 Jan 2012 12:52:51 +0400 Subject: SUNRPC: parametrize local rpcbind clients creation with net ns These client are per network namespace and thus can be created for different network namespaces. Signed-off-by: Stanislav Kinsbursky Signed-off-by: Trond Myklebust --- include/linux/sunrpc/clnt.h | 4 ++-- net/sunrpc/rpcb_clnt.c | 7 +++---- net/sunrpc/svc.c | 4 ++-- 3 files changed, 7 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sunrpc/clnt.h b/include/linux/sunrpc/clnt.h index b0b3e572a619..e891a8a18cb3 100644 --- a/include/linux/sunrpc/clnt.h +++ b/include/linux/sunrpc/clnt.h @@ -136,8 +136,8 @@ void rpc_shutdown_client(struct rpc_clnt *); void rpc_release_client(struct rpc_clnt *); void rpc_task_release_client(struct rpc_task *); -int rpcb_create_local(void); -void rpcb_put_local(void); +int rpcb_create_local(struct net *); +void rpcb_put_local(struct net *); int rpcb_register(struct net *, u32, u32, int, unsigned short); int rpcb_v4_register(struct net *net, const u32 program, const u32 version, diff --git a/net/sunrpc/rpcb_clnt.c b/net/sunrpc/rpcb_clnt.c index a5aa50d0aae4..6d6a84f67449 100644 --- a/net/sunrpc/rpcb_clnt.c +++ b/net/sunrpc/rpcb_clnt.c @@ -175,9 +175,9 @@ static int rpcb_get_local(struct net *net) return cnt; } -void rpcb_put_local(void) +void rpcb_put_local(struct net *net) { - struct sunrpc_net *sn = net_generic(&init_net, sunrpc_net_id); + struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); struct rpc_clnt *clnt = sn->rpcb_local_clnt; struct rpc_clnt *clnt4 = sn->rpcb_local_clnt4; int shutdown; @@ -323,11 +323,10 @@ out: * Returns zero on success, otherwise a negative errno value * is returned. */ -int rpcb_create_local(void) +int rpcb_create_local(struct net *net) { static DEFINE_MUTEX(rpcb_create_local_mutex); int result = 0; - struct net *net = &init_net; if (rpcb_get_local(net)) return result; diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index 889d7b11b9ac..0aee925fbd73 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -372,7 +372,7 @@ static int svc_rpcb_setup(struct svc_serv *serv) { int err; - err = rpcb_create_local(); + err = rpcb_create_local(&init_net); if (err) return err; @@ -384,7 +384,7 @@ static int svc_rpcb_setup(struct svc_serv *serv) void svc_rpcb_cleanup(struct svc_serv *serv) { svc_unregister(serv); - rpcb_put_local(); + rpcb_put_local(&init_net); } EXPORT_SYMBOL_GPL(svc_rpcb_cleanup); -- cgit v1.2.3 From 90100b1766c914c820baa78b5be6845fae1159b8 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Fri, 13 Jan 2012 13:09:19 +0400 Subject: SUNRPC: parametrize rpc_pton() by network context Parametrize rpc_pton() by network context and thus force it's callers to pass in network context instead of using hard-coded "init_net". Signed-off-by: Stanislav Kinsbursky Signed-off-by: Trond Myklebust --- fs/nfs/dns_resolve.c | 4 ++-- fs/nfs/nfs4filelayoutdev.c | 2 +- fs/nfs/nfs4namespace.c | 2 +- fs/nfs/super.c | 4 ++-- fs/nfsd/nfsctl.c | 2 +- include/linux/sunrpc/clnt.h | 2 +- net/sunrpc/addr.c | 7 ++++--- net/sunrpc/svcauth_unix.c | 2 +- 8 files changed, 13 insertions(+), 12 deletions(-) (limited to 'include/linux') diff --git a/fs/nfs/dns_resolve.c b/fs/nfs/dns_resolve.c index 200eb67c95d9..7edc62a8a64f 100644 --- a/fs/nfs/dns_resolve.c +++ b/fs/nfs/dns_resolve.c @@ -20,7 +20,7 @@ ssize_t nfs_dns_resolve_name(struct net *net, char *name, size_t namelen, ip_len = dns_query(NULL, name, namelen, NULL, &ip_addr, NULL); if (ip_len > 0) - ret = rpc_pton(ip_addr, ip_len, sa, salen); + ret = rpc_pton(&init_net, ip_addr, ip_len, sa, salen); else ret = -ESRCH; kfree(ip_addr); @@ -224,7 +224,7 @@ static int nfs_dns_parse(struct cache_detail *cd, char *buf, int buflen) len = qword_get(&buf, buf1, sizeof(buf1)); if (len <= 0) goto out; - key.addrlen = rpc_pton(buf1, len, + key.addrlen = rpc_pton(&init_net, buf1, len, (struct sockaddr *)&key.addr, sizeof(key.addr)); diff --git a/fs/nfs/nfs4filelayoutdev.c b/fs/nfs/nfs4filelayoutdev.c index 8ae91908f5aa..0d8b9523a3cb 100644 --- a/fs/nfs/nfs4filelayoutdev.c +++ b/fs/nfs/nfs4filelayoutdev.c @@ -457,7 +457,7 @@ decode_ds_addr(struct xdr_stream *streamp, gfp_t gfp_flags) INIT_LIST_HEAD(&da->da_node); - if (!rpc_pton(buf, portstr-buf, (struct sockaddr *)&da->da_addr, + if (!rpc_pton(&init_net, buf, portstr-buf, (struct sockaddr *)&da->da_addr, sizeof(da->da_addr))) { dprintk("%s: error parsing address %s\n", __func__, buf); goto out_free_da; diff --git a/fs/nfs/nfs4namespace.c b/fs/nfs/nfs4namespace.c index 919a36935924..48a9acdbaeb6 100644 --- a/fs/nfs/nfs4namespace.c +++ b/fs/nfs/nfs4namespace.c @@ -98,7 +98,7 @@ static size_t nfs_parse_server_name(char *string, size_t len, { ssize_t ret; - ret = rpc_pton(string, len, sa, salen); + ret = rpc_pton(&init_net, string, len, sa, salen); if (ret == 0) { ret = nfs_dns_resolve_name(server->client->cl_xprt->xprt_net, string, len, sa, salen); diff --git a/fs/nfs/super.c b/fs/nfs/super.c index e45feb0fee59..b79f2a11c29e 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -1408,7 +1408,7 @@ static int nfs_parse_mount_options(char *raw, if (string == NULL) goto out_nomem; mnt->nfs_server.addrlen = - rpc_pton(string, strlen(string), + rpc_pton(&init_net, string, strlen(string), (struct sockaddr *) &mnt->nfs_server.address, sizeof(mnt->nfs_server.address)); @@ -1430,7 +1430,7 @@ static int nfs_parse_mount_options(char *raw, if (string == NULL) goto out_nomem; mnt->mount_server.addrlen = - rpc_pton(string, strlen(string), + rpc_pton(&init_net, string, strlen(string), (struct sockaddr *) &mnt->mount_server.address, sizeof(mnt->mount_server.address)); diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index 748eda93ce59..330352d379b6 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -223,7 +223,7 @@ static ssize_t write_unlock_ip(struct file *file, char *buf, size_t size) if (qword_get(&buf, fo_path, size) < 0) return -EINVAL; - if (rpc_pton(fo_path, size, sap, salen) == 0) + if (rpc_pton(&init_net, fo_path, size, sap, salen) == 0) return -EINVAL; return nlmsvc_unlock_all_by_ip(sap); diff --git a/include/linux/sunrpc/clnt.h b/include/linux/sunrpc/clnt.h index e891a8a18cb3..7bd114fc84e3 100644 --- a/include/linux/sunrpc/clnt.h +++ b/include/linux/sunrpc/clnt.h @@ -163,7 +163,7 @@ size_t rpc_peeraddr(struct rpc_clnt *, struct sockaddr *, size_t); const char *rpc_peeraddr2str(struct rpc_clnt *, enum rpc_display_format_t); size_t rpc_ntop(const struct sockaddr *, char *, const size_t); -size_t rpc_pton(const char *, const size_t, +size_t rpc_pton(struct net *, const char *, const size_t, struct sockaddr *, const size_t); char * rpc_sockaddr2uaddr(const struct sockaddr *, gfp_t); size_t rpc_uaddr2sockaddr(const char *, const size_t, diff --git a/net/sunrpc/addr.c b/net/sunrpc/addr.c index 18c40a2002fb..82b06b73c1e1 100644 --- a/net/sunrpc/addr.c +++ b/net/sunrpc/addr.c @@ -230,6 +230,7 @@ static size_t rpc_pton6(struct net *net, const char *buf, const size_t buflen, /** * rpc_pton - Construct a sockaddr in @sap + * @net: applicable network namespace * @buf: C string containing presentation format IP address * @buflen: length of presentation address in bytes * @sap: buffer into which to plant socket address @@ -242,14 +243,14 @@ static size_t rpc_pton6(struct net *net, const char *buf, const size_t buflen, * socket address, if successful. Returns zero if an error * occurred. */ -size_t rpc_pton(const char *buf, const size_t buflen, +size_t rpc_pton(struct net *net, const char *buf, const size_t buflen, struct sockaddr *sap, const size_t salen) { unsigned int i; for (i = 0; i < buflen; i++) if (buf[i] == ':') - return rpc_pton6(&init_net, buf, buflen, sap, salen); + return rpc_pton6(net, buf, buflen, sap, salen); return rpc_pton4(buf, buflen, sap, salen); } EXPORT_SYMBOL_GPL(rpc_pton); @@ -340,7 +341,7 @@ size_t rpc_uaddr2sockaddr(const char *uaddr, const size_t uaddr_len, port = (unsigned short)((porthi << 8) | portlo); *c = '\0'; - if (rpc_pton(buf, strlen(buf), sap, salen) == 0) + if (rpc_pton(&init_net, buf, strlen(buf), sap, salen) == 0) return 0; switch (sap->sa_family) { diff --git a/net/sunrpc/svcauth_unix.c b/net/sunrpc/svcauth_unix.c index 01153ead1dba..2f8c426c1384 100644 --- a/net/sunrpc/svcauth_unix.c +++ b/net/sunrpc/svcauth_unix.c @@ -211,7 +211,7 @@ static int ip_map_parse(struct cache_detail *cd, len = qword_get(&mesg, buf, mlen); if (len <= 0) return -EINVAL; - if (rpc_pton(buf, len, &address.sa, sizeof(address)) == 0) + if (rpc_pton(&init_net, buf, len, &address.sa, sizeof(address)) == 0) return -EINVAL; switch (address.sa.sa_family) { case AF_INET: -- cgit v1.2.3 From f2ac4dc911fdbc9b98a6a48b40efc45aa9161775 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Fri, 13 Jan 2012 13:09:27 +0400 Subject: SUNRPC: parametrize rpc_uaddr2sockaddr() by network context Parametrize rpc_uaddr2sockaddr() by network context and thus force it's callers to pass in network context instead of using hard-coded "init_net". Signed-off-by: Stanislav Kinsbursky Signed-off-by: Trond Myklebust --- fs/nfsd/nfs4state.c | 2 +- include/linux/sunrpc/clnt.h | 2 +- net/sunrpc/addr.c | 8 +++++--- net/sunrpc/rpcb_clnt.c | 2 +- 4 files changed, 8 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index e8c98f009670..c5cddd659429 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -1308,7 +1308,7 @@ gen_callback(struct nfs4_client *clp, struct nfsd4_setclientid *se, struct svc_r else goto out_err; - conn->cb_addrlen = rpc_uaddr2sockaddr(se->se_callback_addr_val, + conn->cb_addrlen = rpc_uaddr2sockaddr(&init_net, se->se_callback_addr_val, se->se_callback_addr_len, (struct sockaddr *)&conn->cb_addr, sizeof(conn->cb_addr)); diff --git a/include/linux/sunrpc/clnt.h b/include/linux/sunrpc/clnt.h index 7bd114fc84e3..9e754e3458fc 100644 --- a/include/linux/sunrpc/clnt.h +++ b/include/linux/sunrpc/clnt.h @@ -166,7 +166,7 @@ size_t rpc_ntop(const struct sockaddr *, char *, const size_t); size_t rpc_pton(struct net *, const char *, const size_t, struct sockaddr *, const size_t); char * rpc_sockaddr2uaddr(const struct sockaddr *, gfp_t); -size_t rpc_uaddr2sockaddr(const char *, const size_t, +size_t rpc_uaddr2sockaddr(struct net *, const char *, const size_t, struct sockaddr *, const size_t); static inline unsigned short rpc_get_port(const struct sockaddr *sap) diff --git a/net/sunrpc/addr.c b/net/sunrpc/addr.c index 82b06b73c1e1..d11418f97f1f 100644 --- a/net/sunrpc/addr.c +++ b/net/sunrpc/addr.c @@ -297,6 +297,7 @@ char *rpc_sockaddr2uaddr(const struct sockaddr *sap, gfp_t gfp_flags) /** * rpc_uaddr2sockaddr - convert a universal address to a socket address. + * @net: applicable network namespace * @uaddr: C string containing universal address to convert * @uaddr_len: length of universal address string * @sap: buffer into which to plant socket address @@ -308,8 +309,9 @@ char *rpc_sockaddr2uaddr(const struct sockaddr *sap, gfp_t gfp_flags) * Returns the size of the socket address if successful; otherwise * zero is returned. */ -size_t rpc_uaddr2sockaddr(const char *uaddr, const size_t uaddr_len, - struct sockaddr *sap, const size_t salen) +size_t rpc_uaddr2sockaddr(struct net *net, const char *uaddr, + const size_t uaddr_len, struct sockaddr *sap, + const size_t salen) { char *c, buf[RPCBIND_MAXUADDRLEN + sizeof('\0')]; unsigned long portlo, porthi; @@ -341,7 +343,7 @@ size_t rpc_uaddr2sockaddr(const char *uaddr, const size_t uaddr_len, port = (unsigned short)((porthi << 8) | portlo); *c = '\0'; - if (rpc_pton(&init_net, buf, strlen(buf), sap, salen) == 0) + if (rpc_pton(net, buf, strlen(buf), sap, salen) == 0) return 0; switch (sap->sa_family) { diff --git a/net/sunrpc/rpcb_clnt.c b/net/sunrpc/rpcb_clnt.c index 6d6a84f67449..7c6fac004447 100644 --- a/net/sunrpc/rpcb_clnt.c +++ b/net/sunrpc/rpcb_clnt.c @@ -934,7 +934,7 @@ static int rpcb_dec_getaddr(struct rpc_rqst *req, struct xdr_stream *xdr, dprintk("RPC: %5u RPCB_%s reply: %s\n", task->tk_pid, task->tk_msg.rpc_proc->p_name, (char *)p); - if (rpc_uaddr2sockaddr((char *)p, len, sap, sizeof(address)) == 0) + if (rpc_uaddr2sockaddr(&init_net, (char *)p, len, sap, sizeof(address)) == 0) goto out_fail; rpcb->r_port = rpc_get_port(sap); -- cgit v1.2.3 From 5247fab5c82779174d50590e0200bf532248a8a1 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Fri, 13 Jan 2012 14:02:48 +0400 Subject: SUNRPC: pass network namespace to service registering routines Lockd and NFSd services will handle requests from and to many network nsamespaces. And thus have to be registered and unregistered per network namespace. Signed-off-by: Stanislav Kinsbursky Signed-off-by: Trond Myklebust --- include/linux/sunrpc/svc.h | 2 +- net/sunrpc/svc.c | 42 +++++++++++++++++++++++------------------- net/sunrpc/svcsock.c | 3 ++- 3 files changed, 26 insertions(+), 21 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h index 35b37b1e9299..d3563c2a5808 100644 --- a/include/linux/sunrpc/svc.h +++ b/include/linux/sunrpc/svc.h @@ -428,7 +428,7 @@ void svc_destroy(struct svc_serv *); int svc_process(struct svc_rqst *); int bc_svc_process(struct svc_serv *, struct rpc_rqst *, struct svc_rqst *); -int svc_register(const struct svc_serv *, const int, +int svc_register(const struct svc_serv *, struct net *, const int, const unsigned short, const unsigned short); void svc_wake_up(struct svc_serv *); diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index 0aee925fbd73..a2d3330b70de 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -30,7 +30,7 @@ #define RPCDBG_FACILITY RPCDBG_SVCDSP -static void svc_unregister(const struct svc_serv *serv); +static void svc_unregister(const struct svc_serv *serv, struct net *net); #define svc_serv_is_pooled(serv) ((serv)->sv_function) @@ -377,13 +377,13 @@ static int svc_rpcb_setup(struct svc_serv *serv) return err; /* Remove any stale portmap registrations */ - svc_unregister(serv); + svc_unregister(serv, &init_net); return 0; } void svc_rpcb_cleanup(struct svc_serv *serv) { - svc_unregister(serv); + svc_unregister(serv, &init_net); rpcb_put_local(&init_net); } EXPORT_SYMBOL_GPL(svc_rpcb_cleanup); @@ -795,7 +795,8 @@ EXPORT_SYMBOL_GPL(svc_exit_thread); * Returns zero on success; a negative errno value is returned * if any error occurs. */ -static int __svc_rpcb_register4(const u32 program, const u32 version, +static int __svc_rpcb_register4(struct net *net, const u32 program, + const u32 version, const unsigned short protocol, const unsigned short port) { @@ -818,7 +819,7 @@ static int __svc_rpcb_register4(const u32 program, const u32 version, return -ENOPROTOOPT; } - error = rpcb_v4_register(&init_net, program, version, + error = rpcb_v4_register(net, program, version, (const struct sockaddr *)&sin, netid); /* @@ -826,7 +827,7 @@ static int __svc_rpcb_register4(const u32 program, const u32 version, * registration request with the legacy rpcbind v2 protocol. */ if (error == -EPROTONOSUPPORT) - error = rpcb_register(&init_net, program, version, protocol, port); + error = rpcb_register(net, program, version, protocol, port); return error; } @@ -842,7 +843,8 @@ static int __svc_rpcb_register4(const u32 program, const u32 version, * Returns zero on success; a negative errno value is returned * if any error occurs. */ -static int __svc_rpcb_register6(const u32 program, const u32 version, +static int __svc_rpcb_register6(struct net *net, const u32 program, + const u32 version, const unsigned short protocol, const unsigned short port) { @@ -865,7 +867,7 @@ static int __svc_rpcb_register6(const u32 program, const u32 version, return -ENOPROTOOPT; } - error = rpcb_v4_register(&init_net, program, version, + error = rpcb_v4_register(net, program, version, (const struct sockaddr *)&sin6, netid); /* @@ -885,7 +887,7 @@ static int __svc_rpcb_register6(const u32 program, const u32 version, * Returns zero on success; a negative errno value is returned * if any error occurs. */ -static int __svc_register(const char *progname, +static int __svc_register(struct net *net, const char *progname, const u32 program, const u32 version, const int family, const unsigned short protocol, @@ -895,12 +897,12 @@ static int __svc_register(const char *progname, switch (family) { case PF_INET: - error = __svc_rpcb_register4(program, version, + error = __svc_rpcb_register4(net, program, version, protocol, port); break; #if IS_ENABLED(CONFIG_IPV6) case PF_INET6: - error = __svc_rpcb_register6(program, version, + error = __svc_rpcb_register6(net, program, version, protocol, port); #endif } @@ -914,14 +916,16 @@ static int __svc_register(const char *progname, /** * svc_register - register an RPC service with the local portmapper * @serv: svc_serv struct for the service to register + * @net: net namespace for the service to register * @family: protocol family of service's listener socket * @proto: transport protocol number to advertise * @port: port to advertise * * Service is registered for any address in the passed-in protocol family */ -int svc_register(const struct svc_serv *serv, const int family, - const unsigned short proto, const unsigned short port) +int svc_register(const struct svc_serv *serv, struct net *net, + const int family, const unsigned short proto, + const unsigned short port) { struct svc_program *progp; unsigned int i; @@ -946,7 +950,7 @@ int svc_register(const struct svc_serv *serv, const int family, if (progp->pg_vers[i]->vs_hidden) continue; - error = __svc_register(progp->pg_name, progp->pg_prog, + error = __svc_register(net, progp->pg_name, progp->pg_prog, i, family, proto, port); if (error < 0) break; @@ -963,19 +967,19 @@ int svc_register(const struct svc_serv *serv, const int family, * any "inet6" entries anyway. So a PMAP_UNSET should be sufficient * in this case to clear all existing entries for [program, version]. */ -static void __svc_unregister(const u32 program, const u32 version, +static void __svc_unregister(struct net *net, const u32 program, const u32 version, const char *progname) { int error; - error = rpcb_v4_register(&init_net, program, version, NULL, ""); + error = rpcb_v4_register(net, program, version, NULL, ""); /* * User space didn't support rpcbind v4, so retry this * request with the legacy rpcbind v2 protocol. */ if (error == -EPROTONOSUPPORT) - error = rpcb_register(&init_net, program, version, 0, 0); + error = rpcb_register(net, program, version, 0, 0); dprintk("svc: %s(%sv%u), error %d\n", __func__, progname, version, error); @@ -989,7 +993,7 @@ static void __svc_unregister(const u32 program, const u32 version, * The result of unregistration is reported via dprintk for those who want * verification of the result, but is otherwise not important. */ -static void svc_unregister(const struct svc_serv *serv) +static void svc_unregister(const struct svc_serv *serv, struct net *net) { struct svc_program *progp; unsigned long flags; @@ -1006,7 +1010,7 @@ static void svc_unregister(const struct svc_serv *serv) dprintk("svc: attempting to unregister %sv%u\n", progp->pg_name, i); - __svc_unregister(progp->pg_prog, i, progp->pg_name); + __svc_unregister(net, progp->pg_prog, i, progp->pg_name); } } diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c index 464570906f80..e8af0c9e436b 100644 --- a/net/sunrpc/svcsock.c +++ b/net/sunrpc/svcsock.c @@ -1409,7 +1409,8 @@ static struct svc_sock *svc_setup_socket(struct svc_serv *serv, /* Register socket with portmapper */ if (*errp >= 0 && pmap_register) - *errp = svc_register(serv, inet->sk_family, inet->sk_protocol, + *errp = svc_register(serv, sock->sk->sk_net, inet->sk_family, + inet->sk_protocol, ntohs(inet_sk(inet)->inet_sport)); if (*errp < 0) { -- cgit v1.2.3 From 5ecebb7c7fd737cf387a552994df319c063973db Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Fri, 13 Jan 2012 14:03:04 +0400 Subject: SUNRPC: unregister service on creation in current network namespace On service shutdown we can be sure, that no more users of it left except current. Thus it looks like using current network namespace context is safe in this case. Signed-off-by: Stanislav Kinsbursky Signed-off-by: Trond Myklebust --- fs/nfsd/nfssvc.c | 4 ++-- include/linux/sunrpc/svc.h | 9 +++++---- net/sunrpc/svc.c | 14 +++++++------- 3 files changed, 14 insertions(+), 13 deletions(-) (limited to 'include/linux') diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c index eda7d7e55e05..fce472f5f39e 100644 --- a/fs/nfsd/nfssvc.c +++ b/fs/nfsd/nfssvc.c @@ -251,13 +251,13 @@ static void nfsd_shutdown(void) nfsd_up = false; } -static void nfsd_last_thread(struct svc_serv *serv) +static void nfsd_last_thread(struct svc_serv *serv, struct net *net) { /* When last nfsd thread exits we need to do some clean-up */ nfsd_serv = NULL; nfsd_shutdown(); - svc_rpcb_cleanup(serv); + svc_rpcb_cleanup(serv, net); printk(KERN_WARNING "nfsd: last server has exited, flushing export " "cache\n"); diff --git a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h index d3563c2a5808..7b65495aa4ef 100644 --- a/include/linux/sunrpc/svc.h +++ b/include/linux/sunrpc/svc.h @@ -84,7 +84,8 @@ struct svc_serv { unsigned int sv_nrpools; /* number of thread pools */ struct svc_pool * sv_pools; /* array of thread pools */ - void (*sv_shutdown)(struct svc_serv *serv); + void (*sv_shutdown)(struct svc_serv *serv, + struct net *net); /* Callback to use when last thread * exits. */ @@ -413,14 +414,14 @@ struct svc_procedure { /* * Function prototypes. */ -void svc_rpcb_cleanup(struct svc_serv *serv); +void svc_rpcb_cleanup(struct svc_serv *serv, struct net *net); struct svc_serv *svc_create(struct svc_program *, unsigned int, - void (*shutdown)(struct svc_serv *)); + void (*shutdown)(struct svc_serv *, struct net *net)); struct svc_rqst *svc_prepare_thread(struct svc_serv *serv, struct svc_pool *pool, int node); void svc_exit_thread(struct svc_rqst *); struct svc_serv * svc_create_pooled(struct svc_program *, unsigned int, - void (*shutdown)(struct svc_serv *), + void (*shutdown)(struct svc_serv *, struct net *net), svc_thread_fn, struct module *); int svc_set_num_threads(struct svc_serv *, struct svc_pool *, int); int svc_pool_stats_open(struct svc_serv *serv, struct file *file); diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index cb2caaee2af9..a8b49a044619 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -382,10 +382,10 @@ static int svc_rpcb_setup(struct svc_serv *serv, struct net *net) return 0; } -void svc_rpcb_cleanup(struct svc_serv *serv) +void svc_rpcb_cleanup(struct svc_serv *serv, struct net *net) { - svc_unregister(serv, &init_net); - rpcb_put_local(&init_net); + svc_unregister(serv, net); + rpcb_put_local(net); } EXPORT_SYMBOL_GPL(svc_rpcb_cleanup); @@ -411,7 +411,7 @@ static int svc_uses_rpcbind(struct svc_serv *serv) */ static struct svc_serv * __svc_create(struct svc_program *prog, unsigned int bufsize, int npools, - void (*shutdown)(struct svc_serv *serv)) + void (*shutdown)(struct svc_serv *serv, struct net *net)) { struct svc_serv *serv; unsigned int vers; @@ -485,7 +485,7 @@ __svc_create(struct svc_program *prog, unsigned int bufsize, int npools, struct svc_serv * svc_create(struct svc_program *prog, unsigned int bufsize, - void (*shutdown)(struct svc_serv *serv)) + void (*shutdown)(struct svc_serv *serv, struct net *net)) { return __svc_create(prog, bufsize, /*npools*/1, shutdown); } @@ -493,7 +493,7 @@ EXPORT_SYMBOL_GPL(svc_create); struct svc_serv * svc_create_pooled(struct svc_program *prog, unsigned int bufsize, - void (*shutdown)(struct svc_serv *serv), + void (*shutdown)(struct svc_serv *serv, struct net *net), svc_thread_fn func, struct module *mod) { struct svc_serv *serv; @@ -542,7 +542,7 @@ svc_destroy(struct svc_serv *serv) svc_close_all(serv); if (serv->sv_shutdown) - serv->sv_shutdown(serv); + serv->sv_shutdown(serv, current->nsproxy->net_ns); cache_clean_deferred(serv); -- cgit v1.2.3 From 0a402d5a653ee2b613aaba3092a87b1e964622ce Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Thu, 19 Jan 2012 21:42:21 +0400 Subject: SUNRPC: cache creation and destruction routines introduced This patch prepares infrastructure for network namespace aware cache detail allocation. One note about adding network namespace link to cache structure. It's going to be used later in NFS DNS cache parsing routine (nfs_dns_parse for rpc_pton() call). Signed-off-by: Stanislav Kinsbursky Signed-off-by: Trond Myklebust Acked-by: J. Bruce Fields --- include/linux/sunrpc/cache.h | 4 ++++ net/sunrpc/cache.c | 26 ++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) (limited to 'include/linux') diff --git a/include/linux/sunrpc/cache.h b/include/linux/sunrpc/cache.h index 590a8ab0cec3..259381ca811b 100644 --- a/include/linux/sunrpc/cache.h +++ b/include/linux/sunrpc/cache.h @@ -117,6 +117,7 @@ struct cache_detail { struct cache_detail_procfs procfs; struct cache_detail_pipefs pipefs; } u; + struct net *net; }; @@ -202,6 +203,9 @@ extern int cache_register_net(struct cache_detail *cd, struct net *net); extern void cache_unregister(struct cache_detail *cd); extern void cache_unregister_net(struct cache_detail *cd, struct net *net); +extern struct cache_detail *cache_create_net(struct cache_detail *tmpl, struct net *net); +extern void cache_destroy_net(struct cache_detail *cd, struct net *net); + extern void sunrpc_init_cache_detail(struct cache_detail *cd); extern void sunrpc_destroy_cache_detail(struct cache_detail *cd); extern int sunrpc_cache_register_pipefs(struct dentry *parent, const char *, diff --git a/net/sunrpc/cache.c b/net/sunrpc/cache.c index fefe06729f9d..a450b8ac648b 100644 --- a/net/sunrpc/cache.c +++ b/net/sunrpc/cache.c @@ -1664,6 +1664,32 @@ void cache_unregister(struct cache_detail *cd) } EXPORT_SYMBOL_GPL(cache_unregister); +struct cache_detail *cache_create_net(struct cache_detail *tmpl, struct net *net) +{ + struct cache_detail *cd; + + cd = kmemdup(tmpl, sizeof(struct cache_detail), GFP_KERNEL); + if (cd == NULL) + return ERR_PTR(-ENOMEM); + + cd->hash_table = kzalloc(cd->hash_size * sizeof(struct cache_head *), + GFP_KERNEL); + if (cd->hash_table == NULL) { + kfree(cd); + return ERR_PTR(-ENOMEM); + } + cd->net = net; + return cd; +} +EXPORT_SYMBOL_GPL(cache_create_net); + +void cache_destroy_net(struct cache_detail *cd, struct net *net) +{ + kfree(cd->hash_table); + kfree(cd); +} +EXPORT_SYMBOL_GPL(cache_destroy_net); + static ssize_t cache_read_pipefs(struct file *filp, char __user *buf, size_t count, loff_t *ppos) { -- cgit v1.2.3 From a1db410d0bbadc49943f0fcddb21702ceb429396 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Thu, 19 Jan 2012 21:42:37 +0400 Subject: SUNRPC: create GSS auth cache per network namespace This patch makes GSS auth cache details allocated and registered per network namespace context. Thus with this patch rsi_cache and rsc_cache contents for network namespace "X" are controlled from proc file system mount for the same network namespace "X". Signed-off-by: Stanislav Kinsbursky Signed-off-by: Trond Myklebust Acked-by: J. Bruce Fields --- include/linux/sunrpc/svcauth_gss.h | 2 + net/sunrpc/auth_gss/auth_gss.c | 21 +++++ net/sunrpc/auth_gss/svcauth_gss.c | 165 ++++++++++++++++++++++++++----------- net/sunrpc/netns.h | 2 + net/sunrpc/sunrpc_syms.c | 1 + 5 files changed, 141 insertions(+), 50 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sunrpc/svcauth_gss.h b/include/linux/sunrpc/svcauth_gss.h index 83bbee3f089c..7c32daa025eb 100644 --- a/include/linux/sunrpc/svcauth_gss.h +++ b/include/linux/sunrpc/svcauth_gss.h @@ -18,6 +18,8 @@ int gss_svc_init(void); void gss_svc_shutdown(void); +int gss_svc_init_net(struct net *net); +void gss_svc_shutdown_net(struct net *net); int svcauth_gss_register_pseudoflavor(u32 pseudoflavor, char * name); u32 svcauth_gss_flavor(struct auth_domain *dom); char *svc_gss_principal(struct svc_rqst *); diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index 5ebb602cabe0..cb2e56452748 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c @@ -1662,6 +1662,21 @@ static const struct rpc_pipe_ops gss_upcall_ops_v1 = { .release_pipe = gss_pipe_release, }; +static __net_init int rpcsec_gss_init_net(struct net *net) +{ + return gss_svc_init_net(net); +} + +static __net_exit void rpcsec_gss_exit_net(struct net *net) +{ + gss_svc_shutdown_net(net); +} + +static struct pernet_operations rpcsec_gss_net_ops = { + .init = rpcsec_gss_init_net, + .exit = rpcsec_gss_exit_net, +}; + /* * Initialize RPCSEC_GSS module */ @@ -1675,8 +1690,13 @@ static int __init init_rpcsec_gss(void) err = gss_svc_init(); if (err) goto out_unregister; + err = register_pernet_subsys(&rpcsec_gss_net_ops); + if (err) + goto out_svc_exit; rpc_init_wait_queue(&pipe_version_rpc_waitqueue, "gss pipe version"); return 0; +out_svc_exit: + gss_svc_shutdown(); out_unregister: rpcauth_unregister(&authgss_ops); out: @@ -1685,6 +1705,7 @@ out: static void __exit exit_rpcsec_gss(void) { + unregister_pernet_subsys(&rpcsec_gss_net_ops); gss_svc_shutdown(); rpcauth_unregister(&authgss_ops); rcu_barrier(); /* Wait for completion of call_rcu()'s */ diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c index 8d0f7d3c71c8..1600cfb1618c 100644 --- a/net/sunrpc/auth_gss/svcauth_gss.c +++ b/net/sunrpc/auth_gss/svcauth_gss.c @@ -48,6 +48,8 @@ #include #include +#include "../netns.h" + #ifdef RPC_DEBUG # define RPCDBG_FACILITY RPCDBG_AUTH #endif @@ -75,10 +77,8 @@ struct rsi { int major_status, minor_status; }; -static struct cache_head *rsi_table[RSI_HASHMAX]; -static struct cache_detail rsi_cache; -static struct rsi *rsi_update(struct rsi *new, struct rsi *old); -static struct rsi *rsi_lookup(struct rsi *item); +static struct rsi *rsi_update(struct cache_detail *cd, struct rsi *new, struct rsi *old); +static struct rsi *rsi_lookup(struct cache_detail *cd, struct rsi *item); static void rsi_free(struct rsi *rsii) { @@ -216,7 +216,7 @@ static int rsi_parse(struct cache_detail *cd, if (dup_to_netobj(&rsii.in_token, buf, len)) goto out; - rsip = rsi_lookup(&rsii); + rsip = rsi_lookup(cd, &rsii); if (!rsip) goto out; @@ -258,21 +258,20 @@ static int rsi_parse(struct cache_detail *cd, if (dup_to_netobj(&rsii.out_token, buf, len)) goto out; rsii.h.expiry_time = expiry; - rsip = rsi_update(&rsii, rsip); + rsip = rsi_update(cd, &rsii, rsip); status = 0; out: rsi_free(&rsii); if (rsip) - cache_put(&rsip->h, &rsi_cache); + cache_put(&rsip->h, cd); else status = -ENOMEM; return status; } -static struct cache_detail rsi_cache = { +static struct cache_detail rsi_cache_template = { .owner = THIS_MODULE, .hash_size = RSI_HASHMAX, - .hash_table = rsi_table, .name = "auth.rpcsec.init", .cache_put = rsi_put, .cache_upcall = rsi_upcall, @@ -283,24 +282,24 @@ static struct cache_detail rsi_cache = { .alloc = rsi_alloc, }; -static struct rsi *rsi_lookup(struct rsi *item) +static struct rsi *rsi_lookup(struct cache_detail *cd, struct rsi *item) { struct cache_head *ch; int hash = rsi_hash(item); - ch = sunrpc_cache_lookup(&rsi_cache, &item->h, hash); + ch = sunrpc_cache_lookup(cd, &item->h, hash); if (ch) return container_of(ch, struct rsi, h); else return NULL; } -static struct rsi *rsi_update(struct rsi *new, struct rsi *old) +static struct rsi *rsi_update(struct cache_detail *cd, struct rsi *new, struct rsi *old) { struct cache_head *ch; int hash = rsi_hash(new); - ch = sunrpc_cache_update(&rsi_cache, &new->h, + ch = sunrpc_cache_update(cd, &new->h, &old->h, hash); if (ch) return container_of(ch, struct rsi, h); @@ -339,10 +338,8 @@ struct rsc { char *client_name; }; -static struct cache_head *rsc_table[RSC_HASHMAX]; -static struct cache_detail rsc_cache; -static struct rsc *rsc_update(struct rsc *new, struct rsc *old); -static struct rsc *rsc_lookup(struct rsc *item); +static struct rsc *rsc_update(struct cache_detail *cd, struct rsc *new, struct rsc *old); +static struct rsc *rsc_lookup(struct cache_detail *cd, struct rsc *item); static void rsc_free(struct rsc *rsci) { @@ -444,7 +441,7 @@ static int rsc_parse(struct cache_detail *cd, if (expiry == 0) goto out; - rscp = rsc_lookup(&rsci); + rscp = rsc_lookup(cd, &rsci); if (!rscp) goto out; @@ -506,22 +503,21 @@ static int rsc_parse(struct cache_detail *cd, } rsci.h.expiry_time = expiry; - rscp = rsc_update(&rsci, rscp); + rscp = rsc_update(cd, &rsci, rscp); status = 0; out: gss_mech_put(gm); rsc_free(&rsci); if (rscp) - cache_put(&rscp->h, &rsc_cache); + cache_put(&rscp->h, cd); else status = -ENOMEM; return status; } -static struct cache_detail rsc_cache = { +static struct cache_detail rsc_cache_template = { .owner = THIS_MODULE, .hash_size = RSC_HASHMAX, - .hash_table = rsc_table, .name = "auth.rpcsec.context", .cache_put = rsc_put, .cache_parse = rsc_parse, @@ -531,24 +527,24 @@ static struct cache_detail rsc_cache = { .alloc = rsc_alloc, }; -static struct rsc *rsc_lookup(struct rsc *item) +static struct rsc *rsc_lookup(struct cache_detail *cd, struct rsc *item) { struct cache_head *ch; int hash = rsc_hash(item); - ch = sunrpc_cache_lookup(&rsc_cache, &item->h, hash); + ch = sunrpc_cache_lookup(cd, &item->h, hash); if (ch) return container_of(ch, struct rsc, h); else return NULL; } -static struct rsc *rsc_update(struct rsc *new, struct rsc *old) +static struct rsc *rsc_update(struct cache_detail *cd, struct rsc *new, struct rsc *old) { struct cache_head *ch; int hash = rsc_hash(new); - ch = sunrpc_cache_update(&rsc_cache, &new->h, + ch = sunrpc_cache_update(cd, &new->h, &old->h, hash); if (ch) return container_of(ch, struct rsc, h); @@ -558,7 +554,7 @@ static struct rsc *rsc_update(struct rsc *new, struct rsc *old) static struct rsc * -gss_svc_searchbyctx(struct xdr_netobj *handle) +gss_svc_searchbyctx(struct cache_detail *cd, struct xdr_netobj *handle) { struct rsc rsci; struct rsc *found; @@ -566,11 +562,11 @@ gss_svc_searchbyctx(struct xdr_netobj *handle) memset(&rsci, 0, sizeof(rsci)); if (dup_to_netobj(&rsci.handle, handle->data, handle->len)) return NULL; - found = rsc_lookup(&rsci); + found = rsc_lookup(cd, &rsci); rsc_free(&rsci); if (!found) return NULL; - if (cache_check(&rsc_cache, &found->h, NULL)) + if (cache_check(cd, &found->h, NULL)) return NULL; return found; } @@ -968,20 +964,20 @@ svcauth_gss_set_client(struct svc_rqst *rqstp) } static inline int -gss_write_init_verf(struct svc_rqst *rqstp, struct rsi *rsip) +gss_write_init_verf(struct cache_detail *cd, struct svc_rqst *rqstp, struct rsi *rsip) { struct rsc *rsci; int rc; if (rsip->major_status != GSS_S_COMPLETE) return gss_write_null_verf(rqstp); - rsci = gss_svc_searchbyctx(&rsip->out_handle); + rsci = gss_svc_searchbyctx(cd, &rsip->out_handle); if (rsci == NULL) { rsip->major_status = GSS_S_NO_CONTEXT; return gss_write_null_verf(rqstp); } rc = gss_write_verf(rqstp, rsci->mechctx, GSS_SEQ_WIN); - cache_put(&rsci->h, &rsc_cache); + cache_put(&rsci->h, cd); return rc; } @@ -1000,6 +996,7 @@ static int svcauth_gss_handle_init(struct svc_rqst *rqstp, struct xdr_netobj tmpobj; struct rsi *rsip, rsikey; int ret; + struct sunrpc_net *sn = net_generic(rqstp->rq_xprt->xpt_net, sunrpc_net_id); /* Read the verifier; should be NULL: */ *authp = rpc_autherr_badverf; @@ -1028,17 +1025,17 @@ static int svcauth_gss_handle_init(struct svc_rqst *rqstp, } /* Perform upcall, or find upcall result: */ - rsip = rsi_lookup(&rsikey); + rsip = rsi_lookup(sn->rsi_cache, &rsikey); rsi_free(&rsikey); if (!rsip) return SVC_CLOSE; - if (cache_check(&rsi_cache, &rsip->h, &rqstp->rq_chandle) < 0) + if (cache_check(sn->rsi_cache, &rsip->h, &rqstp->rq_chandle) < 0) /* No upcall result: */ return SVC_CLOSE; ret = SVC_CLOSE; /* Got an answer to the upcall; use it: */ - if (gss_write_init_verf(rqstp, rsip)) + if (gss_write_init_verf(sn->rsc_cache, rqstp, rsip)) goto out; if (resv->iov_len + 4 > PAGE_SIZE) goto out; @@ -1055,7 +1052,7 @@ static int svcauth_gss_handle_init(struct svc_rqst *rqstp, ret = SVC_COMPLETE; out: - cache_put(&rsip->h, &rsi_cache); + cache_put(&rsip->h, sn->rsi_cache); return ret; } @@ -1079,6 +1076,7 @@ svcauth_gss_accept(struct svc_rqst *rqstp, __be32 *authp) __be32 *rpcstart; __be32 *reject_stat = resv->iov_base + resv->iov_len; int ret; + struct sunrpc_net *sn = net_generic(rqstp->rq_xprt->xpt_net, sunrpc_net_id); dprintk("RPC: svcauth_gss: argv->iov_len = %zd\n", argv->iov_len); @@ -1129,7 +1127,7 @@ svcauth_gss_accept(struct svc_rqst *rqstp, __be32 *authp) case RPC_GSS_PROC_DESTROY: /* Look up the context, and check the verifier: */ *authp = rpcsec_gsserr_credproblem; - rsci = gss_svc_searchbyctx(&gc->gc_ctx); + rsci = gss_svc_searchbyctx(sn->rsc_cache, &gc->gc_ctx); if (!rsci) goto auth_err; switch (gss_verify_header(rqstp, rsci, rpcstart, gc, authp)) { @@ -1209,7 +1207,7 @@ drop: ret = SVC_DROP; out: if (rsci) - cache_put(&rsci->h, &rsc_cache); + cache_put(&rsci->h, sn->rsc_cache); return ret; } @@ -1362,6 +1360,7 @@ svcauth_gss_release(struct svc_rqst *rqstp) struct rpc_gss_wire_cred *gc = &gsd->clcred; struct xdr_buf *resbuf = &rqstp->rq_res; int stat = -EINVAL; + struct sunrpc_net *sn = net_generic(rqstp->rq_xprt->xpt_net, sunrpc_net_id); if (gc->gc_proc != RPC_GSS_PROC_DATA) goto out; @@ -1404,7 +1403,7 @@ out_err: put_group_info(rqstp->rq_cred.cr_group_info); rqstp->rq_cred.cr_group_info = NULL; if (gsd->rsci) - cache_put(&gsd->rsci->h, &rsc_cache); + cache_put(&gsd->rsci->h, sn->rsc_cache); gsd->rsci = NULL; return stat; @@ -1429,30 +1428,96 @@ static struct auth_ops svcauthops_gss = { .set_client = svcauth_gss_set_client, }; +static int rsi_cache_create_net(struct net *net) +{ + struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); + struct cache_detail *cd; + int err; + + cd = cache_create_net(&rsi_cache_template, net); + if (IS_ERR(cd)) + return PTR_ERR(cd); + err = cache_register_net(cd, net); + if (err) { + cache_destroy_net(cd, net); + return err; + } + sn->rsi_cache = cd; + return 0; +} + +static void rsi_cache_destroy_net(struct net *net) +{ + struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); + struct cache_detail *cd = sn->rsi_cache; + + sn->rsi_cache = NULL; + cache_purge(cd); + cache_unregister_net(cd, net); + cache_destroy_net(cd, net); +} + +static int rsc_cache_create_net(struct net *net) +{ + struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); + struct cache_detail *cd; + int err; + + cd = cache_create_net(&rsc_cache_template, net); + if (IS_ERR(cd)) + return PTR_ERR(cd); + err = cache_register_net(cd, net); + if (err) { + cache_destroy_net(cd, net); + return err; + } + sn->rsc_cache = cd; + return 0; +} + +static void rsc_cache_destroy_net(struct net *net) +{ + struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); + struct cache_detail *cd = sn->rsc_cache; + + sn->rsc_cache = NULL; + cache_purge(cd); + cache_unregister_net(cd, net); + cache_destroy_net(cd, net); +} + int -gss_svc_init(void) +gss_svc_init_net(struct net *net) { - int rv = svc_auth_register(RPC_AUTH_GSS, &svcauthops_gss); + int rv; + + rv = rsc_cache_create_net(net); if (rv) return rv; - rv = cache_register(&rsc_cache); + rv = rsi_cache_create_net(net); if (rv) goto out1; - rv = cache_register(&rsi_cache); - if (rv) - goto out2; return 0; -out2: - cache_unregister(&rsc_cache); out1: - svc_auth_unregister(RPC_AUTH_GSS); + rsc_cache_destroy_net(net); return rv; } +void +gss_svc_shutdown_net(struct net *net) +{ + rsi_cache_destroy_net(net); + rsc_cache_destroy_net(net); +} + +int +gss_svc_init(void) +{ + return svc_auth_register(RPC_AUTH_GSS, &svcauthops_gss); +} + void gss_svc_shutdown(void) { - cache_unregister(&rsc_cache); - cache_unregister(&rsi_cache); svc_auth_unregister(RPC_AUTH_GSS); } diff --git a/net/sunrpc/netns.h b/net/sunrpc/netns.h index 309f88ddb060..ce7bd449173d 100644 --- a/net/sunrpc/netns.h +++ b/net/sunrpc/netns.h @@ -10,6 +10,8 @@ struct sunrpc_net { struct proc_dir_entry *proc_net_rpc; struct cache_detail *ip_map_cache; struct cache_detail *unix_gid_cache; + struct cache_detail *rsc_cache; + struct cache_detail *rsi_cache; struct super_block *pipefs_sb; struct mutex pipefs_sb_lock; diff --git a/net/sunrpc/sunrpc_syms.c b/net/sunrpc/sunrpc_syms.c index 38a72a1b465b..d16ac088f6d8 100644 --- a/net/sunrpc/sunrpc_syms.c +++ b/net/sunrpc/sunrpc_syms.c @@ -25,6 +25,7 @@ #include "netns.h" int sunrpc_net_id; +EXPORT_SYMBOL_GPL(sunrpc_net_id); extern int unix_gid_cache_create(struct net *net); extern int unix_gid_cache_destroy(struct net *net); -- cgit v1.2.3 From 2c5f846747526e2b83c5f1b8e69016be0e2e87c0 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Thu, 19 Jan 2012 21:42:53 +0400 Subject: SUNRPC: generic cache register routines removed All cache users now uses network-namespace-aware routines, so generic ones are obsolete. Signed-off-by: Stanislav Kinsbursky Signed-off-by: Trond Myklebust Acked-by: J. Bruce Fields --- include/linux/sunrpc/cache.h | 2 -- net/sunrpc/cache.c | 12 ------------ 2 files changed, 14 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sunrpc/cache.h b/include/linux/sunrpc/cache.h index 259381ca811b..f5fd6160dbca 100644 --- a/include/linux/sunrpc/cache.h +++ b/include/linux/sunrpc/cache.h @@ -198,9 +198,7 @@ extern void cache_flush(void); extern void cache_purge(struct cache_detail *detail); #define NEVER (0x7FFFFFFF) extern void __init cache_initialize(void); -extern int cache_register(struct cache_detail *cd); extern int cache_register_net(struct cache_detail *cd, struct net *net); -extern void cache_unregister(struct cache_detail *cd); extern void cache_unregister_net(struct cache_detail *cd, struct net *net); extern struct cache_detail *cache_create_net(struct cache_detail *tmpl, struct net *net); diff --git a/net/sunrpc/cache.c b/net/sunrpc/cache.c index a450b8ac648b..f21ece088764 100644 --- a/net/sunrpc/cache.c +++ b/net/sunrpc/cache.c @@ -1645,12 +1645,6 @@ int cache_register_net(struct cache_detail *cd, struct net *net) } EXPORT_SYMBOL_GPL(cache_register_net); -int cache_register(struct cache_detail *cd) -{ - return cache_register_net(cd, &init_net); -} -EXPORT_SYMBOL_GPL(cache_register); - void cache_unregister_net(struct cache_detail *cd, struct net *net) { remove_cache_proc_entries(cd, net); @@ -1658,12 +1652,6 @@ void cache_unregister_net(struct cache_detail *cd, struct net *net) } EXPORT_SYMBOL_GPL(cache_unregister_net); -void cache_unregister(struct cache_detail *cd) -{ - cache_unregister_net(cd, &init_net); -} -EXPORT_SYMBOL_GPL(cache_unregister); - struct cache_detail *cache_create_net(struct cache_detail *tmpl, struct net *net) { struct cache_detail *cd; -- cgit v1.2.3 From ec7652aaf261b7dcb368344369df1e99886c7cd2 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Tue, 6 Dec 2011 16:42:40 +0300 Subject: SUNRPC: register RPC stats /proc entries in passed network namespace context This patch makes it possible to create NFS program entry ("/proc/net/rpc/nfs") in passed network namespace context instead of hard-coded "init_net". Signed-off-by: Stanislav Kinsbursky Signed-off-by: Trond Myklebust --- fs/nfs/inode.c | 6 +++--- include/linux/sunrpc/stats.h | 8 ++++---- net/sunrpc/stats.c | 15 ++++++++------- 3 files changed, 15 insertions(+), 14 deletions(-) (limited to 'include/linux') diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 65486e652943..d2c760e193f4 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -1625,14 +1625,14 @@ static int __init init_nfs_fs(void) goto out0; #ifdef CONFIG_PROC_FS - rpc_proc_register(&nfs_rpcstat); + rpc_proc_register(&init_net, &nfs_rpcstat); #endif if ((err = register_nfs_fs()) != 0) goto out; return 0; out: #ifdef CONFIG_PROC_FS - rpc_proc_unregister("nfs"); + rpc_proc_unregister(&init_net, "nfs"); #endif nfs_destroy_directcache(); out0: @@ -1671,7 +1671,7 @@ static void __exit exit_nfs_fs(void) nfs_dns_resolver_destroy(); nfs_idmap_quit(); #ifdef CONFIG_PROC_FS - rpc_proc_unregister("nfs"); + rpc_proc_unregister(&init_net, "nfs"); #endif nfs_cleanup_cb_ident_idr(); unregister_nfs_fs(); diff --git a/include/linux/sunrpc/stats.h b/include/linux/sunrpc/stats.h index 680471d1f28a..f625b5746bdc 100644 --- a/include/linux/sunrpc/stats.h +++ b/include/linux/sunrpc/stats.h @@ -58,8 +58,8 @@ void rpc_modcount(struct inode *, int); #endif #ifdef CONFIG_PROC_FS -struct proc_dir_entry * rpc_proc_register(struct rpc_stat *); -void rpc_proc_unregister(const char *); +struct proc_dir_entry * rpc_proc_register(struct net *,struct rpc_stat *); +void rpc_proc_unregister(struct net *,const char *); void rpc_proc_zero(struct rpc_program *); struct proc_dir_entry * svc_proc_register(struct svc_stat *, const struct file_operations *); @@ -69,8 +69,8 @@ void svc_seq_show(struct seq_file *, const struct svc_stat *); #else -static inline struct proc_dir_entry *rpc_proc_register(struct rpc_stat *s) { return NULL; } -static inline void rpc_proc_unregister(const char *p) {} +static inline struct proc_dir_entry *rpc_proc_register(struct net *, struct rpc_stat *s) { return NULL; } +static inline void rpc_proc_unregisterstruct net *, (const char *p) {} static inline void rpc_proc_zero(struct rpc_program *p) {} static inline struct proc_dir_entry *svc_proc_register(struct svc_stat *s, diff --git a/net/sunrpc/stats.c b/net/sunrpc/stats.c index 80df89d957ba..f0f6e7ceadd5 100644 --- a/net/sunrpc/stats.c +++ b/net/sunrpc/stats.c @@ -213,28 +213,29 @@ EXPORT_SYMBOL_GPL(rpc_print_iostats); * Register/unregister RPC proc files */ static inline struct proc_dir_entry * -do_register(const char *name, void *data, const struct file_operations *fops) +do_register(struct net *net, const char *name, void *data, + const struct file_operations *fops) { struct sunrpc_net *sn; dprintk("RPC: registering /proc/net/rpc/%s\n", name); - sn = net_generic(&init_net, sunrpc_net_id); + sn = net_generic(net, sunrpc_net_id); return proc_create_data(name, 0, sn->proc_net_rpc, fops, data); } struct proc_dir_entry * -rpc_proc_register(struct rpc_stat *statp) +rpc_proc_register(struct net *net, struct rpc_stat *statp) { - return do_register(statp->program->name, statp, &rpc_proc_fops); + return do_register(net, statp->program->name, statp, &rpc_proc_fops); } EXPORT_SYMBOL_GPL(rpc_proc_register); void -rpc_proc_unregister(const char *name) +rpc_proc_unregister(struct net *net, const char *name) { struct sunrpc_net *sn; - sn = net_generic(&init_net, sunrpc_net_id); + sn = net_generic(net, sunrpc_net_id); remove_proc_entry(name, sn->proc_net_rpc); } EXPORT_SYMBOL_GPL(rpc_proc_unregister); @@ -242,7 +243,7 @@ EXPORT_SYMBOL_GPL(rpc_proc_unregister); struct proc_dir_entry * svc_proc_register(struct svc_stat *statp, const struct file_operations *fops) { - return do_register(statp->program->pg_name, statp, fops); + return do_register(&init_net, statp->program->pg_name, statp, fops); } EXPORT_SYMBOL_GPL(svc_proc_register); -- cgit v1.2.3 From cc9f4be5ffb86b4b483eeb15eb08aebc60a1b9a9 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Tue, 24 Jan 2012 03:28:07 +0000 Subject: sunrpc: fix stats.h for CONFIG_PROC_FS not enabled Fix build errors in linux/sunrpc/stats.h when CONFIG_PROC_FS is not enabled: - add parameter names to inline functions - fix placement of '(' in rpc_proc_unregister() Fixes these errors: include/linux/sunrpc/stats.h:72:63: error: parameter name omitted include/linux/sunrpc/stats.h:73:46: error: expected '=', ',', ';', 'asm' or '__attribute__' before 'net' Signed-off-by: Randy Dunlap Signed-off-by: Trond Myklebust --- include/linux/sunrpc/stats.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sunrpc/stats.h b/include/linux/sunrpc/stats.h index f625b5746bdc..c5aaf7ddb5cf 100644 --- a/include/linux/sunrpc/stats.h +++ b/include/linux/sunrpc/stats.h @@ -69,8 +69,8 @@ void svc_seq_show(struct seq_file *, const struct svc_stat *); #else -static inline struct proc_dir_entry *rpc_proc_register(struct net *, struct rpc_stat *s) { return NULL; } -static inline void rpc_proc_unregisterstruct net *, (const char *p) {} +static inline struct proc_dir_entry *rpc_proc_register(struct net *net, struct rpc_stat *s) { return NULL; } +static inline void rpc_proc_unregister(struct net *net, const char *p) {} static inline void rpc_proc_zero(struct rpc_program *p) {} static inline struct proc_dir_entry *svc_proc_register(struct svc_stat *s, -- cgit v1.2.3 From 246590f56c9f281d60b7dd7efa0818307e65600d Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Tue, 6 Dec 2011 16:42:49 +0300 Subject: SUNRPC: register service stats /proc entries in passed network namespace context This patch makes it possible to create NFSd program entry ("/proc/net/rpc/nfsd") in passed network namespace context instead of hard-coded "init_net". Signed-off-by: Stanislav Kinsbursky Signed-off-by: Trond Myklebust --- fs/nfsd/stats.c | 5 +++-- include/linux/sunrpc/stats.h | 8 ++++---- net/sunrpc/stats.c | 8 ++++---- 3 files changed, 11 insertions(+), 10 deletions(-) (limited to 'include/linux') diff --git a/fs/nfsd/stats.c b/fs/nfsd/stats.c index a2e2402b2afb..6d4521feb6e3 100644 --- a/fs/nfsd/stats.c +++ b/fs/nfsd/stats.c @@ -25,6 +25,7 @@ #include #include #include +#include #include "nfsd.h" @@ -94,11 +95,11 @@ static const struct file_operations nfsd_proc_fops = { void nfsd_stat_init(void) { - svc_proc_register(&nfsd_svcstats, &nfsd_proc_fops); + svc_proc_register(&init_net, &nfsd_svcstats, &nfsd_proc_fops); } void nfsd_stat_shutdown(void) { - svc_proc_unregister("nfsd"); + svc_proc_unregister(&init_net, "nfsd"); } diff --git a/include/linux/sunrpc/stats.h b/include/linux/sunrpc/stats.h index c5aaf7ddb5cf..76f3f7cc6e33 100644 --- a/include/linux/sunrpc/stats.h +++ b/include/linux/sunrpc/stats.h @@ -61,9 +61,9 @@ void rpc_modcount(struct inode *, int); struct proc_dir_entry * rpc_proc_register(struct net *,struct rpc_stat *); void rpc_proc_unregister(struct net *,const char *); void rpc_proc_zero(struct rpc_program *); -struct proc_dir_entry * svc_proc_register(struct svc_stat *, +struct proc_dir_entry * svc_proc_register(struct net *, struct svc_stat *, const struct file_operations *); -void svc_proc_unregister(const char *); +void svc_proc_unregister(struct net *, const char *); void svc_seq_show(struct seq_file *, const struct svc_stat *); @@ -73,9 +73,9 @@ static inline struct proc_dir_entry *rpc_proc_register(struct net *net, struct r static inline void rpc_proc_unregister(struct net *net, const char *p) {} static inline void rpc_proc_zero(struct rpc_program *p) {} -static inline struct proc_dir_entry *svc_proc_register(struct svc_stat *s, +static inline struct proc_dir_entry *svc_proc_register(struct net *net, struct svc_stat *s, const struct file_operations *f) { return NULL; } -static inline void svc_proc_unregister(const char *p) {} +static inline void svc_proc_unregister(struct net *net, const char *p) {} static inline void svc_seq_show(struct seq_file *seq, const struct svc_stat *st) {} diff --git a/net/sunrpc/stats.c b/net/sunrpc/stats.c index f0f6e7ceadd5..3c4f6888c891 100644 --- a/net/sunrpc/stats.c +++ b/net/sunrpc/stats.c @@ -241,18 +241,18 @@ rpc_proc_unregister(struct net *net, const char *name) EXPORT_SYMBOL_GPL(rpc_proc_unregister); struct proc_dir_entry * -svc_proc_register(struct svc_stat *statp, const struct file_operations *fops) +svc_proc_register(struct net *net, struct svc_stat *statp, const struct file_operations *fops) { - return do_register(&init_net, statp->program->pg_name, statp, fops); + return do_register(net, statp->program->pg_name, statp, fops); } EXPORT_SYMBOL_GPL(svc_proc_register); void -svc_proc_unregister(const char *name) +svc_proc_unregister(struct net *net, const char *name) { struct sunrpc_net *sn; - sn = net_generic(&init_net, sunrpc_net_id); + sn = net_generic(net, sunrpc_net_id); remove_proc_entry(name, sn->proc_net_rpc); } EXPORT_SYMBOL_GPL(svc_proc_unregister); -- cgit v1.2.3 From 4cb54ca2069903121e4c03ec427147c47bed5755 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Fri, 20 Jan 2012 16:50:53 +0400 Subject: SUNRPC: search for service transports in network namespace context Service transports are parametrized by network namespace. And thus lookup of transport instance have to take network namespace into account. Signed-off-by: Stanislav Kinsbursky Signed-off-by: Trond Myklebust Acked-by: J. Bruce Fields --- fs/lockd/svc.c | 2 +- fs/nfsd/nfsctl.c | 4 ++-- include/linux/sunrpc/svc_xprt.h | 3 ++- net/sunrpc/svc_xprt.c | 6 +++++- 4 files changed, 10 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c index c061b9aa7ddb..ff379ff7761f 100644 --- a/fs/lockd/svc.c +++ b/fs/lockd/svc.c @@ -193,7 +193,7 @@ static int create_lockd_listener(struct svc_serv *serv, const char *name, { struct svc_xprt *xprt; - xprt = svc_find_xprt(serv, name, family, 0); + xprt = svc_find_xprt(serv, name, &init_net, family, 0); if (xprt == NULL) return svc_create_xprt(serv, name, &init_net, family, port, SVC_SOCK_DEFAULTS); diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index 330352d379b6..64c24af8d7ea 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -722,7 +722,7 @@ static ssize_t __write_ports_addxprt(char *buf) nfsd_serv->sv_nrthreads--; return 0; out_close: - xprt = svc_find_xprt(nfsd_serv, transport, PF_INET, port); + xprt = svc_find_xprt(nfsd_serv, transport, &init_net, PF_INET, port); if (xprt != NULL) { svc_close_xprt(xprt); svc_xprt_put(xprt); @@ -748,7 +748,7 @@ static ssize_t __write_ports_delxprt(char *buf) if (port < 1 || port > USHRT_MAX || nfsd_serv == NULL) return -EINVAL; - xprt = svc_find_xprt(nfsd_serv, transport, AF_UNSPEC, port); + xprt = svc_find_xprt(nfsd_serv, transport, &init_net, AF_UNSPEC, port); if (xprt == NULL) return -ENOTCONN; diff --git a/include/linux/sunrpc/svc_xprt.h b/include/linux/sunrpc/svc_xprt.h index dfa900948af7..b3f64b12f141 100644 --- a/include/linux/sunrpc/svc_xprt.h +++ b/include/linux/sunrpc/svc_xprt.h @@ -121,7 +121,8 @@ void svc_close_xprt(struct svc_xprt *xprt); int svc_port_is_privileged(struct sockaddr *sin); int svc_print_xprts(char *buf, int maxlen); struct svc_xprt *svc_find_xprt(struct svc_serv *serv, const char *xcl_name, - const sa_family_t af, const unsigned short port); + struct net *net, const sa_family_t af, + const unsigned short port); int svc_xprt_names(struct svc_serv *serv, char *buf, const int buflen); static inline void svc_xprt_get(struct svc_xprt *xprt) diff --git a/net/sunrpc/svc_xprt.c b/net/sunrpc/svc_xprt.c index 74cb0d8e9ca1..de1e1a8b526b 100644 --- a/net/sunrpc/svc_xprt.c +++ b/net/sunrpc/svc_xprt.c @@ -1089,6 +1089,7 @@ static struct svc_deferred_req *svc_deferred_dequeue(struct svc_xprt *xprt) * svc_find_xprt - find an RPC transport instance * @serv: pointer to svc_serv to search * @xcl_name: C string containing transport's class name + * @net: owner net pointer * @af: Address family of transport's local address * @port: transport's IP port number * @@ -1101,7 +1102,8 @@ static struct svc_deferred_req *svc_deferred_dequeue(struct svc_xprt *xprt) * service's list that has a matching class name. */ struct svc_xprt *svc_find_xprt(struct svc_serv *serv, const char *xcl_name, - const sa_family_t af, const unsigned short port) + struct net *net, const sa_family_t af, + const unsigned short port) { struct svc_xprt *xprt; struct svc_xprt *found = NULL; @@ -1112,6 +1114,8 @@ struct svc_xprt *svc_find_xprt(struct svc_serv *serv, const char *xcl_name, spin_lock_bh(&serv->sv_lock); list_for_each_entry(xprt, &serv->sv_permsocks, xpt_list) { + if (xprt->xpt_net != net) + continue; if (strcmp(xprt->xpt_class->xcl_name, xcl_name)) continue; if (af != AF_UNSPEC && af != xprt->xpt_local.ss_family) -- cgit v1.2.3 From 6eac7d3f45a2519283d38bf670cb6968230124f8 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Fri, 20 Jan 2012 13:53:37 -0500 Subject: SUNRPC: constify rpc_clnt fields cl_server and cl_protname ...and get rid of the superfluous cl_inline_name. Signed-off-by: Trond Myklebust --- include/linux/sunrpc/clnt.h | 9 ++++----- net/sunrpc/clnt.c | 29 ++++++++++++----------------- net/sunrpc/rpcb_clnt.c | 2 +- 3 files changed, 17 insertions(+), 23 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sunrpc/clnt.h b/include/linux/sunrpc/clnt.h index 9e754e3458fc..db6970ced9bc 100644 --- a/include/linux/sunrpc/clnt.h +++ b/include/linux/sunrpc/clnt.h @@ -41,8 +41,8 @@ struct rpc_clnt { cl_vers, /* RPC version number */ cl_maxproc; /* max procedure number */ - char * cl_server; /* server machine name */ - char * cl_protname; /* protocol name */ + const char * cl_server; /* server machine name */ + const char * cl_protname; /* protocol name */ struct rpc_auth * cl_auth; /* authenticator */ struct rpc_stat * cl_stats; /* per-program statistics */ struct rpc_iostats * cl_metrics; /* per-client statistics */ @@ -62,7 +62,6 @@ struct rpc_clnt { struct rpc_rtt cl_rtt_default; struct rpc_timeout cl_timeout_default; struct rpc_program * cl_program; - char cl_inline_name[32]; char *cl_principal; /* target to authenticate to */ }; @@ -97,7 +96,7 @@ struct rpc_procinfo { unsigned int p_count; /* call count */ unsigned int p_timer; /* Which RTT timer to use */ u32 p_statidx; /* Which procedure to account */ - char * p_name; /* name of procedure */ + const char * p_name; /* name of procedure */ }; #ifdef __KERNEL__ @@ -109,7 +108,7 @@ struct rpc_create_args { size_t addrsize; struct sockaddr *saddress; const struct rpc_timeout *timeout; - char *servername; + const char *servername; struct rpc_program *program; u32 prognumber; /* overrides program->number */ u32 version; diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index 4c6848017168..e9b22e8e16c7 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -273,15 +273,9 @@ static struct rpc_clnt * rpc_new_client(const struct rpc_create_args *args, stru goto out_err; clnt->cl_parent = clnt; - clnt->cl_server = clnt->cl_inline_name; - if (len > sizeof(clnt->cl_inline_name)) { - char *buf = kmalloc(len, GFP_KERNEL); - if (buf != NULL) - clnt->cl_server = buf; - else - len = sizeof(clnt->cl_inline_name); - } - strlcpy(clnt->cl_server, args->servername, len); + clnt->cl_server = kstrdup(args->servername, GFP_KERNEL); + if (clnt->cl_server == NULL) + goto out_no_server; clnt->cl_xprt = xprt; clnt->cl_procinfo = version->procs; @@ -346,8 +340,8 @@ out_no_path: out_no_principal: rpc_free_iostats(clnt->cl_metrics); out_no_stats: - if (clnt->cl_server != clnt->cl_inline_name) - kfree(clnt->cl_server); + kfree(clnt->cl_server); +out_no_server: kfree(clnt); out_err: xprt_put(xprt); @@ -470,6 +464,9 @@ rpc_clone_client(struct rpc_clnt *clnt) new = kmemdup(clnt, sizeof(*new), GFP_KERNEL); if (!new) goto out_no_clnt; + new->cl_server = kstrdup(clnt->cl_server, GFP_KERNEL); + if (new->cl_server == NULL) + goto out_no_server; new->cl_parent = clnt; /* Turn off autobind on clones */ new->cl_autobind = 0; @@ -500,6 +497,8 @@ out_no_path: out_no_principal: rpc_free_iostats(new->cl_metrics); out_no_stats: + kfree(new->cl_server); +out_no_server: kfree(new); out_no_clnt: dprintk("RPC: %s: returned error %d\n", __func__, err); @@ -565,13 +564,9 @@ rpc_free_client(struct rpc_clnt *clnt) { dprintk("RPC: destroying %s client for %s\n", clnt->cl_protname, clnt->cl_server); - if (clnt->cl_parent != clnt) { + if (clnt->cl_parent != clnt) rpc_release_client(clnt->cl_parent); - goto out_free; - } - if (clnt->cl_server != clnt->cl_inline_name) - kfree(clnt->cl_server); -out_free: + kfree(clnt->cl_server); rpc_unregister_client(clnt); rpc_clnt_remove_pipedir(clnt); rpc_free_iostats(clnt->cl_metrics); diff --git a/net/sunrpc/rpcb_clnt.c b/net/sunrpc/rpcb_clnt.c index 4ce3a8e02953..d3978017b25d 100644 --- a/net/sunrpc/rpcb_clnt.c +++ b/net/sunrpc/rpcb_clnt.c @@ -343,7 +343,7 @@ out: return result; } -static struct rpc_clnt *rpcb_create(struct net *net, char *hostname, +static struct rpc_clnt *rpcb_create(struct net *net, const char *hostname, struct sockaddr *srvaddr, size_t salen, int proto, u32 version) { -- cgit v1.2.3 From 080b794ce5ad318ce34c52abaedf1bc6788a5abb Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Fri, 20 Jan 2012 13:53:56 -0500 Subject: SUNRPC: constify rpc_program->name Signed-off-by: Trond Myklebust --- include/linux/sunrpc/clnt.h | 4 ++-- net/sunrpc/clnt.c | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sunrpc/clnt.h b/include/linux/sunrpc/clnt.h index db6970ced9bc..4a46ffd73a04 100644 --- a/include/linux/sunrpc/clnt.h +++ b/include/linux/sunrpc/clnt.h @@ -70,12 +70,12 @@ struct rpc_clnt { */ #define RPC_MAXVERSION 4 struct rpc_program { - char * name; /* protocol name */ + const char * name; /* protocol name */ u32 number; /* program number */ unsigned int nrvers; /* number of versions */ struct rpc_version ** version; /* version array */ struct rpc_stat * stats; /* statistics */ - char * pipe_dir_name; /* path to rpc_pipefs dir */ + const char * pipe_dir_name; /* path to rpc_pipefs dir */ }; struct rpc_version { diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index e9b22e8e16c7..1b2317fa4043 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -118,7 +118,8 @@ static void rpc_clnt_remove_pipedir(struct rpc_clnt *clnt) } static struct dentry *rpc_setup_pipedir_sb(struct super_block *sb, - struct rpc_clnt *clnt, char *dir_name) + struct rpc_clnt *clnt, + const char *dir_name) { static uint32_t clntid; char name[15]; @@ -151,7 +152,7 @@ static struct dentry *rpc_setup_pipedir_sb(struct super_block *sb, } static int -rpc_setup_pipedir(struct rpc_clnt *clnt, char *dir_name) +rpc_setup_pipedir(struct rpc_clnt *clnt, const char *dir_name) { struct super_block *pipefs_sb; struct dentry *dentry; -- cgit v1.2.3 From a613fa168afc19179a7547fbba45644c5b6912bf Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Fri, 20 Jan 2012 13:53:56 -0500 Subject: SUNRPC: constify the rpc_program Signed-off-by: Trond Myklebust --- fs/lockd/clnt4xdr.c | 2 +- fs/lockd/clntxdr.c | 8 ++++---- fs/lockd/mon.c | 8 ++++---- fs/nfs/client.c | 8 ++++---- fs/nfs/internal.h | 2 +- fs/nfs/mount_clnt.c | 10 +++++----- fs/nfs/nfs2xdr.c | 2 +- fs/nfs/nfs3xdr.c | 4 ++-- fs/nfs/nfs4xdr.c | 2 +- fs/nfsd/nfs4callback.c | 6 +++--- include/linux/lockd/lockd.h | 2 +- include/linux/lockd/xdr4.h | 2 +- include/linux/nfs_xdr.h | 10 +++++----- include/linux/sunrpc/clnt.h | 8 ++++---- include/linux/sunrpc/stats.h | 6 +++--- net/sunrpc/clnt.c | 8 ++++---- net/sunrpc/rpcb_clnt.c | 20 ++++++++++---------- 17 files changed, 54 insertions(+), 54 deletions(-) (limited to 'include/linux') diff --git a/fs/lockd/clnt4xdr.c b/fs/lockd/clnt4xdr.c index f848b52c67b1..3ddcbb1c0a43 100644 --- a/fs/lockd/clnt4xdr.c +++ b/fs/lockd/clnt4xdr.c @@ -598,7 +598,7 @@ static struct rpc_procinfo nlm4_procedures[] = { PROC(GRANTED_RES, res, norep), }; -struct rpc_version nlm_version4 = { +const struct rpc_version nlm_version4 = { .number = 4, .nrprocs = ARRAY_SIZE(nlm4_procedures), .procs = nlm4_procedures, diff --git a/fs/lockd/clntxdr.c b/fs/lockd/clntxdr.c index 180ac34feb9a..3d35e3e80c1c 100644 --- a/fs/lockd/clntxdr.c +++ b/fs/lockd/clntxdr.c @@ -596,19 +596,19 @@ static struct rpc_procinfo nlm_procedures[] = { PROC(GRANTED_RES, res, norep), }; -static struct rpc_version nlm_version1 = { +static const struct rpc_version nlm_version1 = { .number = 1, .nrprocs = ARRAY_SIZE(nlm_procedures), .procs = nlm_procedures, }; -static struct rpc_version nlm_version3 = { +static const struct rpc_version nlm_version3 = { .number = 3, .nrprocs = ARRAY_SIZE(nlm_procedures), .procs = nlm_procedures, }; -static struct rpc_version *nlm_versions[] = { +static const struct rpc_version *nlm_versions[] = { [1] = &nlm_version1, [3] = &nlm_version3, #ifdef CONFIG_LOCKD_V4 @@ -618,7 +618,7 @@ static struct rpc_version *nlm_versions[] = { static struct rpc_stat nlm_rpc_stats; -struct rpc_program nlm_program = { +const struct rpc_program nlm_program = { .name = "lockd", .number = NLM_PROGRAM, .nrvers = ARRAY_SIZE(nlm_versions), diff --git a/fs/lockd/mon.c b/fs/lockd/mon.c index 65ba36b80a9e..c196030e530a 100644 --- a/fs/lockd/mon.c +++ b/fs/lockd/mon.c @@ -47,7 +47,7 @@ struct nsm_res { u32 state; }; -static struct rpc_program nsm_program; +static const struct rpc_program nsm_program; static LIST_HEAD(nsm_handles); static DEFINE_SPINLOCK(nsm_lock); @@ -534,19 +534,19 @@ static struct rpc_procinfo nsm_procedures[] = { }, }; -static struct rpc_version nsm_version1 = { +static const struct rpc_version nsm_version1 = { .number = 1, .nrprocs = ARRAY_SIZE(nsm_procedures), .procs = nsm_procedures }; -static struct rpc_version * nsm_version[] = { +static const struct rpc_version *nsm_version[] = { [1] = &nsm_version1, }; static struct rpc_stat nsm_stats; -static struct rpc_program nsm_program = { +static const struct rpc_program nsm_program = { .name = "statd", .number = NSM_PROGRAM, .nrvers = ARRAY_SIZE(nsm_version), diff --git a/fs/nfs/client.c b/fs/nfs/client.c index 34c8d1cbf06e..98af1cb28ee3 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -89,7 +89,7 @@ static bool nfs4_disable_idmapping = true; /* * RPC cruft for NFS */ -static struct rpc_version *nfs_version[5] = { +static const struct rpc_version *nfs_version[5] = { [2] = &nfs_version2, #ifdef CONFIG_NFS_V3 [3] = &nfs_version3, @@ -99,7 +99,7 @@ static struct rpc_version *nfs_version[5] = { #endif }; -struct rpc_program nfs_program = { +const struct rpc_program nfs_program = { .name = "nfs", .number = NFS_PROGRAM, .nrvers = ARRAY_SIZE(nfs_version), @@ -115,11 +115,11 @@ struct rpc_stat nfs_rpcstat = { #ifdef CONFIG_NFS_V3_ACL static struct rpc_stat nfsacl_rpcstat = { &nfsacl_program }; -static struct rpc_version * nfsacl_version[] = { +static const struct rpc_version *nfsacl_version[] = { [3] = &nfsacl_version3, }; -struct rpc_program nfsacl_program = { +const struct rpc_program nfsacl_program = { .name = "nfsacl", .number = NFS_ACL_PROGRAM, .nrvers = ARRAY_SIZE(nfsacl_version), diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index eda4cde40fb2..cdb121d3c6f4 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -145,7 +145,7 @@ extern int nfs_mount(struct nfs_mount_request *info); extern void nfs_umount(const struct nfs_mount_request *info); /* client.c */ -extern struct rpc_program nfs_program; +extern const struct rpc_program nfs_program; extern void nfs_cleanup_cb_ident_idr(void); extern void nfs_put_client(struct nfs_client *); diff --git a/fs/nfs/mount_clnt.c b/fs/nfs/mount_clnt.c index 4fbe3a8e5e6b..b37ca34af903 100644 --- a/fs/nfs/mount_clnt.c +++ b/fs/nfs/mount_clnt.c @@ -67,7 +67,7 @@ enum { MOUNTPROC3_EXPORT = 5, }; -static struct rpc_program mnt_program; +static const struct rpc_program mnt_program; /* * Defined by OpenGroup XNFS Version 3W, chapter 8 @@ -488,19 +488,19 @@ static struct rpc_procinfo mnt3_procedures[] = { }; -static struct rpc_version mnt_version1 = { +static const struct rpc_version mnt_version1 = { .number = 1, .nrprocs = ARRAY_SIZE(mnt_procedures), .procs = mnt_procedures, }; -static struct rpc_version mnt_version3 = { +static const struct rpc_version mnt_version3 = { .number = 3, .nrprocs = ARRAY_SIZE(mnt3_procedures), .procs = mnt3_procedures, }; -static struct rpc_version *mnt_version[] = { +static const struct rpc_version *mnt_version[] = { NULL, &mnt_version1, NULL, @@ -509,7 +509,7 @@ static struct rpc_version *mnt_version[] = { static struct rpc_stat mnt_stats; -static struct rpc_program mnt_program = { +static const struct rpc_program mnt_program = { .name = "mount", .number = NFS_MNT_PROGRAM, .nrvers = ARRAY_SIZE(mnt_version), diff --git a/fs/nfs/nfs2xdr.c b/fs/nfs/nfs2xdr.c index 792cb13a4304..1f56000fabbd 100644 --- a/fs/nfs/nfs2xdr.c +++ b/fs/nfs/nfs2xdr.c @@ -1150,7 +1150,7 @@ struct rpc_procinfo nfs_procedures[] = { PROC(STATFS, fhandle, statfsres, 0), }; -struct rpc_version nfs_version2 = { +const struct rpc_version nfs_version2 = { .number = 2, .nrprocs = ARRAY_SIZE(nfs_procedures), .procs = nfs_procedures diff --git a/fs/nfs/nfs3xdr.c b/fs/nfs/nfs3xdr.c index 183c6b123d0f..a77cc9a3ce55 100644 --- a/fs/nfs/nfs3xdr.c +++ b/fs/nfs/nfs3xdr.c @@ -2461,7 +2461,7 @@ struct rpc_procinfo nfs3_procedures[] = { PROC(COMMIT, commit, commit, 5), }; -struct rpc_version nfs_version3 = { +const struct rpc_version nfs_version3 = { .number = 3, .nrprocs = ARRAY_SIZE(nfs3_procedures), .procs = nfs3_procedures @@ -2489,7 +2489,7 @@ static struct rpc_procinfo nfs3_acl_procedures[] = { }, }; -struct rpc_version nfsacl_version3 = { +const struct rpc_version nfsacl_version3 = { .number = 3, .nrprocs = sizeof(nfs3_acl_procedures)/ sizeof(nfs3_acl_procedures[0]), diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 95e92e438407..4633d405a94c 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -7109,7 +7109,7 @@ struct rpc_procinfo nfs4_procedures[] = { #endif /* CONFIG_NFS_V4_1 */ }; -struct rpc_version nfs_version4 = { +const struct rpc_version nfs_version4 = { .number = 4, .nrprocs = ARRAY_SIZE(nfs4_procedures), .procs = nfs4_procedures diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c index 426ccb171650..0e262f32ac41 100644 --- a/fs/nfsd/nfs4callback.c +++ b/fs/nfsd/nfs4callback.c @@ -605,18 +605,18 @@ static struct rpc_version nfs_cb_version4 = { .procs = nfs4_cb_procedures }; -static struct rpc_version *nfs_cb_version[] = { +static const struct rpc_version *nfs_cb_version[] = { &nfs_cb_version4, }; -static struct rpc_program cb_program; +static const struct rpc_program cb_program; static struct rpc_stat cb_stats = { .program = &cb_program }; #define NFS4_CALLBACK 0x40000000 -static struct rpc_program cb_program = { +static const struct rpc_program cb_program = { .name = "nfs4_cb", .number = NFS4_CALLBACK, .nrvers = ARRAY_SIZE(nfs_cb_version), diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h index 88a114fce477..8949167a148d 100644 --- a/include/linux/lockd/lockd.h +++ b/include/linux/lockd/lockd.h @@ -188,7 +188,7 @@ struct nlm_block { /* * Global variables */ -extern struct rpc_program nlm_program; +extern const struct rpc_program nlm_program; extern struct svc_procedure nlmsvc_procedures[]; #ifdef CONFIG_LOCKD_V4 extern struct svc_procedure nlmsvc_procedures4[]; diff --git a/include/linux/lockd/xdr4.h b/include/linux/lockd/xdr4.h index 7353821341ed..e58c88b52ce1 100644 --- a/include/linux/lockd/xdr4.h +++ b/include/linux/lockd/xdr4.h @@ -42,6 +42,6 @@ int nlmclt_encode_lockargs(struct rpc_rqst *, u32 *, struct nlm_args *); int nlmclt_encode_cancargs(struct rpc_rqst *, u32 *, struct nlm_args *); int nlmclt_encode_unlockargs(struct rpc_rqst *, u32 *, struct nlm_args *); */ -extern struct rpc_version nlm_version4; +extern const struct rpc_version nlm_version4; #endif /* LOCKD_XDR4_H */ diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index f5188e10ea0e..144419a9cbd3 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -1274,11 +1274,11 @@ struct nfs_rpc_ops { extern const struct nfs_rpc_ops nfs_v2_clientops; extern const struct nfs_rpc_ops nfs_v3_clientops; extern const struct nfs_rpc_ops nfs_v4_clientops; -extern struct rpc_version nfs_version2; -extern struct rpc_version nfs_version3; -extern struct rpc_version nfs_version4; +extern const struct rpc_version nfs_version2; +extern const struct rpc_version nfs_version3; +extern const struct rpc_version nfs_version4; -extern struct rpc_version nfsacl_version3; -extern struct rpc_program nfsacl_program; +extern const struct rpc_version nfsacl_version3; +extern const struct rpc_program nfsacl_program; #endif diff --git a/include/linux/sunrpc/clnt.h b/include/linux/sunrpc/clnt.h index 4a46ffd73a04..a4c62e95c720 100644 --- a/include/linux/sunrpc/clnt.h +++ b/include/linux/sunrpc/clnt.h @@ -61,7 +61,7 @@ struct rpc_clnt { struct rpc_clnt * cl_parent; /* Points to parent of clones */ struct rpc_rtt cl_rtt_default; struct rpc_timeout cl_timeout_default; - struct rpc_program * cl_program; + const struct rpc_program *cl_program; char *cl_principal; /* target to authenticate to */ }; @@ -73,7 +73,7 @@ struct rpc_program { const char * name; /* protocol name */ u32 number; /* program number */ unsigned int nrvers; /* number of versions */ - struct rpc_version ** version; /* version array */ + const struct rpc_version ** version; /* version array */ struct rpc_stat * stats; /* statistics */ const char * pipe_dir_name; /* path to rpc_pipefs dir */ }; @@ -109,7 +109,7 @@ struct rpc_create_args { struct sockaddr *saddress; const struct rpc_timeout *timeout; const char *servername; - struct rpc_program *program; + const struct rpc_program *program; u32 prognumber; /* overrides program->number */ u32 version; rpc_authflavor_t authflavor; @@ -128,7 +128,7 @@ struct rpc_create_args { struct rpc_clnt *rpc_create(struct rpc_create_args *args); struct rpc_clnt *rpc_bind_new_program(struct rpc_clnt *, - struct rpc_program *, u32); + const struct rpc_program *, u32); void rpc_task_reset_client(struct rpc_task *task, struct rpc_clnt *clnt); struct rpc_clnt *rpc_clone_client(struct rpc_clnt *); void rpc_shutdown_client(struct rpc_clnt *); diff --git a/include/linux/sunrpc/stats.h b/include/linux/sunrpc/stats.h index 76f3f7cc6e33..edc64219f92b 100644 --- a/include/linux/sunrpc/stats.h +++ b/include/linux/sunrpc/stats.h @@ -12,7 +12,7 @@ #include struct rpc_stat { - struct rpc_program * program; + const struct rpc_program *program; unsigned int netcnt, netudpcnt, @@ -60,7 +60,7 @@ void rpc_modcount(struct inode *, int); #ifdef CONFIG_PROC_FS struct proc_dir_entry * rpc_proc_register(struct net *,struct rpc_stat *); void rpc_proc_unregister(struct net *,const char *); -void rpc_proc_zero(struct rpc_program *); +void rpc_proc_zero(const struct rpc_program *); struct proc_dir_entry * svc_proc_register(struct net *, struct svc_stat *, const struct file_operations *); void svc_proc_unregister(struct net *, const char *); @@ -71,7 +71,7 @@ void svc_seq_show(struct seq_file *, static inline struct proc_dir_entry *rpc_proc_register(struct net *net, struct rpc_stat *s) { return NULL; } static inline void rpc_proc_unregister(struct net *net, const char *p) {} -static inline void rpc_proc_zero(struct rpc_program *p) {} +static inline void rpc_proc_zero(const struct rpc_program *p) {} static inline struct proc_dir_entry *svc_proc_register(struct net *net, struct svc_stat *s, const struct file_operations *f) { return NULL; } diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index 1b2317fa4043..db7220d87732 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -238,8 +238,8 @@ void rpc_clients_notifier_unregister(void) static struct rpc_clnt * rpc_new_client(const struct rpc_create_args *args, struct rpc_xprt *xprt) { - struct rpc_program *program = args->program; - struct rpc_version *version; + const struct rpc_program *program = args->program; + const struct rpc_version *version; struct rpc_clnt *clnt = NULL; struct rpc_auth *auth; int err; @@ -626,11 +626,11 @@ rpc_release_client(struct rpc_clnt *clnt) * The Sun NFSv2/v3 ACL protocol can do this. */ struct rpc_clnt *rpc_bind_new_program(struct rpc_clnt *old, - struct rpc_program *program, + const struct rpc_program *program, u32 vers) { struct rpc_clnt *clnt; - struct rpc_version *version; + const struct rpc_version *version; int err; BUG_ON(vers >= program->nrvers || !program->version[vers]); diff --git a/net/sunrpc/rpcb_clnt.c b/net/sunrpc/rpcb_clnt.c index d3978017b25d..b1f08bd67883 100644 --- a/net/sunrpc/rpcb_clnt.c +++ b/net/sunrpc/rpcb_clnt.c @@ -112,7 +112,7 @@ enum { static void rpcb_getport_done(struct rpc_task *, void *); static void rpcb_map_release(void *data); -static struct rpc_program rpcb_program; +static const struct rpc_program rpcb_program; struct rpcbind_args { struct rpc_xprt * r_xprt; @@ -137,8 +137,8 @@ struct rpcb_info { struct rpc_procinfo * rpc_proc; }; -static struct rpcb_info rpcb_next_version[]; -static struct rpcb_info rpcb_next_version6[]; +static const struct rpcb_info rpcb_next_version[]; +static const struct rpcb_info rpcb_next_version6[]; static const struct rpc_call_ops rpcb_getport_ops = { .rpc_call_done = rpcb_getport_done, @@ -1051,7 +1051,7 @@ static struct rpc_procinfo rpcb_procedures4[] = { }, }; -static struct rpcb_info rpcb_next_version[] = { +static const struct rpcb_info rpcb_next_version[] = { { .rpc_vers = RPCBVERS_2, .rpc_proc = &rpcb_procedures2[RPCBPROC_GETPORT], @@ -1061,7 +1061,7 @@ static struct rpcb_info rpcb_next_version[] = { }, }; -static struct rpcb_info rpcb_next_version6[] = { +static const struct rpcb_info rpcb_next_version6[] = { { .rpc_vers = RPCBVERS_4, .rpc_proc = &rpcb_procedures4[RPCBPROC_GETADDR], @@ -1075,25 +1075,25 @@ static struct rpcb_info rpcb_next_version6[] = { }, }; -static struct rpc_version rpcb_version2 = { +static const struct rpc_version rpcb_version2 = { .number = RPCBVERS_2, .nrprocs = ARRAY_SIZE(rpcb_procedures2), .procs = rpcb_procedures2 }; -static struct rpc_version rpcb_version3 = { +static const struct rpc_version rpcb_version3 = { .number = RPCBVERS_3, .nrprocs = ARRAY_SIZE(rpcb_procedures3), .procs = rpcb_procedures3 }; -static struct rpc_version rpcb_version4 = { +static const struct rpc_version rpcb_version4 = { .number = RPCBVERS_4, .nrprocs = ARRAY_SIZE(rpcb_procedures4), .procs = rpcb_procedures4 }; -static struct rpc_version *rpcb_version[] = { +static const struct rpc_version *rpcb_version[] = { NULL, NULL, &rpcb_version2, @@ -1103,7 +1103,7 @@ static struct rpc_version *rpcb_version[] = { static struct rpc_stat rpcb_stats; -static struct rpc_program rpcb_program = { +static const struct rpc_program rpcb_program = { .name = "rpcbind", .number = RPCBIND_PROGRAM, .nrvers = ARRAY_SIZE(rpcb_version), -- cgit v1.2.3 From 82b0a4c3c171b180629696e8d1d5f52516f711e6 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Fri, 20 Jan 2012 14:52:23 -0500 Subject: SUNRPC: Add trace events to the sunrpc subsystem Add declarations to allow tracing of RPC call creation, running, sleeping, and destruction. Signed-off-by: Trond Myklebust --- include/linux/sunrpc/sched.h | 2 +- include/trace/events/sunrpc.h | 124 ++++++++++++++++++++++++++++++++++++++++++ net/sunrpc/sched.c | 12 ++++ 3 files changed, 137 insertions(+), 1 deletion(-) create mode 100644 include/trace/events/sunrpc.h (limited to 'include/linux') diff --git a/include/linux/sunrpc/sched.h b/include/linux/sunrpc/sched.h index bd337f990a41..f7b2df5252b0 100644 --- a/include/linux/sunrpc/sched.h +++ b/include/linux/sunrpc/sched.h @@ -271,7 +271,7 @@ static inline int rpc_task_has_priority(struct rpc_task *task, unsigned char pri } #ifdef RPC_DEBUG -static inline const char * rpc_qname(struct rpc_wait_queue *q) +static inline const char * rpc_qname(const struct rpc_wait_queue *q) { return ((q && q->name) ? q->name : "unknown"); } diff --git a/include/trace/events/sunrpc.h b/include/trace/events/sunrpc.h new file mode 100644 index 000000000000..51cc9490919f --- /dev/null +++ b/include/trace/events/sunrpc.h @@ -0,0 +1,124 @@ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM sunrpc + +#if !defined(_TRACE_SUNRPC_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_SUNRPC_H + +#include +#include +#include + +DECLARE_EVENT_CLASS(rpc_task_running, + + TP_PROTO(const struct rpc_clnt *clnt, const struct rpc_task *task, const void *action), + + TP_ARGS(clnt, task, action), + + TP_STRUCT__entry( + __field(const struct rpc_clnt *, clnt) + __field(const struct rpc_task *, task) + __field(const void *, action) + __field(unsigned long, runstate) + __field(int, status) + __field(unsigned short, flags) + ), + + TP_fast_assign( + __entry->clnt = clnt; + __entry->task = task; + __entry->action = action; + __entry->runstate = task->tk_runstate; + __entry->status = task->tk_status; + __entry->flags = task->tk_flags; + ), + + TP_printk("task:%p@%p flags=%4.4x state=%4.4lx status=%d action=%pf", + __entry->task, + __entry->clnt, + __entry->flags, + __entry->runstate, + __entry->status, + __entry->action + ) +); + +DEFINE_EVENT(rpc_task_running, rpc_task_begin, + + TP_PROTO(const struct rpc_clnt *clnt, const struct rpc_task *task, const void *action), + + TP_ARGS(clnt, task, action) + +); + +DEFINE_EVENT(rpc_task_running, rpc_task_run_action, + + TP_PROTO(const struct rpc_clnt *clnt, const struct rpc_task *task, const void *action), + + TP_ARGS(clnt, task, action) + +); + +DEFINE_EVENT(rpc_task_running, rpc_task_complete, + + TP_PROTO(const struct rpc_clnt *clnt, const struct rpc_task *task, const void *action), + + TP_ARGS(clnt, task, action) + +); + +DECLARE_EVENT_CLASS(rpc_task_queued, + + TP_PROTO(const struct rpc_clnt *clnt, const struct rpc_task *task, const struct rpc_wait_queue *q), + + TP_ARGS(clnt, task, q), + + TP_STRUCT__entry( + __field(const struct rpc_clnt *, clnt) + __field(const struct rpc_task *, task) + __field(const struct rpc_wait_queue *, queue) + __field(unsigned long, timeout) + __field(unsigned long, runstate) + __field(int, status) + __field(unsigned short, flags) + ), + + TP_fast_assign( + __entry->clnt = clnt; + __entry->task = task; + __entry->queue = q; + __entry->timeout = task->tk_timeout; + __entry->runstate = task->tk_runstate; + __entry->status = task->tk_status; + __entry->flags = task->tk_flags; + ), + + TP_printk("task:%p@%p flags=%4.4x state=%4.4lx status=%d timeout=%lu queue=%s", + __entry->task, + __entry->clnt, + __entry->flags, + __entry->runstate, + __entry->status, + __entry->timeout, + rpc_qname(__entry->queue) + ) +); + +DEFINE_EVENT(rpc_task_queued, rpc_task_sleep, + + TP_PROTO(const struct rpc_clnt *clnt, const struct rpc_task *task, const struct rpc_wait_queue *q), + + TP_ARGS(clnt, task, q) + +); + +DEFINE_EVENT(rpc_task_queued, rpc_task_wakeup, + + TP_PROTO(const struct rpc_clnt *clnt, const struct rpc_task *task, const struct rpc_wait_queue *q), + + TP_ARGS(clnt, task, q) + +); + +#endif /* _TRACE_SUNRPC_H */ + +#include diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c index f982dfe53993..d79c63df49b8 100644 --- a/net/sunrpc/sched.c +++ b/net/sunrpc/sched.c @@ -28,6 +28,9 @@ #define RPCDBG_FACILITY RPCDBG_SCHED #endif +#define CREATE_TRACE_POINTS +#include + /* * RPC slabs and memory pools */ @@ -251,6 +254,8 @@ static inline void rpc_task_set_debuginfo(struct rpc_task *task) static void rpc_set_active(struct rpc_task *task) { + trace_rpc_task_begin(task->tk_client, task, NULL); + rpc_task_set_debuginfo(task); set_bit(RPC_TASK_ACTIVE, &task->tk_runstate); } @@ -267,6 +272,8 @@ static int rpc_complete_task(struct rpc_task *task) unsigned long flags; int ret; + trace_rpc_task_complete(task->tk_client, task, NULL); + spin_lock_irqsave(&wq->lock, flags); clear_bit(RPC_TASK_ACTIVE, &task->tk_runstate); ret = atomic_dec_and_test(&task->tk_count); @@ -324,6 +331,8 @@ static void __rpc_sleep_on_priority(struct rpc_wait_queue *q, dprintk("RPC: %5u sleep_on(queue \"%s\" time %lu)\n", task->tk_pid, rpc_qname(q), jiffies); + trace_rpc_task_sleep(task->tk_client, task, q); + __rpc_add_wait_queue(q, task, queue_priority); BUG_ON(task->tk_callback != NULL); @@ -378,6 +387,8 @@ static void __rpc_do_wake_up_task(struct rpc_wait_queue *queue, struct rpc_task return; } + trace_rpc_task_wakeup(task->tk_client, task, queue); + __rpc_remove_wait_queue(queue, task); rpc_make_runnable(task); @@ -701,6 +712,7 @@ static void __rpc_execute(struct rpc_task *task) if (do_action == NULL) break; } + trace_rpc_task_run_action(task->tk_client, task, task->tk_action); do_action(task); /* -- cgit v1.2.3 From 9dfac4fd7f8cdcdf734dff2ccc7ca467f53f1cfd Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 1 Feb 2012 18:02:47 +0100 Subject: pinctrl: delete raw device pointers in pinmux maps After discussion with Mark Brown in an unrelated thread about ADC lookups, it came to my knowledge that the ability to pass a struct device * in the regulator consumers is just a historical artifact, and not really recommended. Since there are no in-kernel users of these pointers, we just kill them right now, before someone starts to use them. Reviewed-by: Mark Brown Signed-off-by: Linus Walleij --- Documentation/pinctrl.txt | 5 +---- drivers/pinctrl/core.c | 22 +++++++--------------- drivers/pinctrl/core.h | 3 +-- drivers/pinctrl/pinconf.c | 8 ++++---- drivers/pinctrl/pinmux.c | 27 ++++++++------------------- include/linux/pinctrl/machine.h | 12 ++---------- 6 files changed, 23 insertions(+), 54 deletions(-) (limited to 'include/linux') diff --git a/Documentation/pinctrl.txt b/Documentation/pinctrl.txt index 150fd3833d0b..14aecd2b2dbc 100644 --- a/Documentation/pinctrl.txt +++ b/Documentation/pinctrl.txt @@ -805,10 +805,7 @@ must match a function provided by the pinmux driver handling this pin range. As you can see we may have several pin controllers on the system and thus we need to specify which one of them that contain the functions we wish -to map. The map can also use struct device * directly, so there is no -inherent need to use strings to specify .dev_name or .ctrl_dev_name, these -are for the situation where you do not have a handle to the struct device *, -for example if they are not yet instantiated or cumbersome to obtain. +to map. You register this pinmux mapping to the pinmux subsystem by simply: diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c index 894cd5e103da..4f10476cc1f2 100644 --- a/drivers/pinctrl/core.c +++ b/drivers/pinctrl/core.c @@ -48,31 +48,23 @@ void *pinctrl_dev_get_drvdata(struct pinctrl_dev *pctldev) EXPORT_SYMBOL_GPL(pinctrl_dev_get_drvdata); /** - * get_pinctrl_dev_from_dev() - look up pin controller device - * @dev: a device pointer, this may be NULL but then devname needs to be - * defined instead - * @devname: the name of a device instance, as returned by dev_name(), this - * may be NULL but then dev needs to be defined instead + * get_pinctrl_dev_from_devname() - look up pin controller device + * @devname: the name of a device instance, as returned by dev_name() * * Looks up a pin control device matching a certain device name or pure device * pointer, the pure device pointer will take precedence. */ -struct pinctrl_dev *get_pinctrl_dev_from_dev(struct device *dev, - const char *devname) +struct pinctrl_dev *get_pinctrl_dev_from_devname(const char *devname) { struct pinctrl_dev *pctldev = NULL; bool found = false; + if (!devname) + return NULL; + mutex_lock(&pinctrldev_list_mutex); list_for_each_entry(pctldev, &pinctrldev_list, node) { - if (dev && pctldev->dev == dev) { - /* Matched on device pointer */ - found = true; - break; - } - - if (devname && - !strcmp(dev_name(pctldev->dev), devname)) { + if (!strcmp(dev_name(pctldev->dev), devname)) { /* Matched on device name */ found = true; break; diff --git a/drivers/pinctrl/core.h b/drivers/pinctrl/core.h index cfa86da6b4b1..8a8b02e9c18e 100644 --- a/drivers/pinctrl/core.h +++ b/drivers/pinctrl/core.h @@ -72,8 +72,7 @@ struct pin_desc { #endif }; -struct pinctrl_dev *get_pinctrl_dev_from_dev(struct device *dev, - const char *dev_name); +struct pinctrl_dev *get_pinctrl_dev_from_devname(const char *dev_name); struct pin_desc *pin_desc_get(struct pinctrl_dev *pctldev, unsigned int pin); int pin_get_from_name(struct pinctrl_dev *pctldev, const char *name); int pinctrl_get_device_gpio_range(unsigned gpio, diff --git a/drivers/pinctrl/pinconf.c b/drivers/pinctrl/pinconf.c index 9fb75456824c..b74f64af1923 100644 --- a/drivers/pinctrl/pinconf.c +++ b/drivers/pinctrl/pinconf.c @@ -51,7 +51,7 @@ int pin_config_get(const char *dev_name, const char *name, struct pinctrl_dev *pctldev; int pin; - pctldev = get_pinctrl_dev_from_dev(NULL, dev_name); + pctldev = get_pinctrl_dev_from_devname(dev_name); if (!pctldev) return -EINVAL; @@ -99,7 +99,7 @@ int pin_config_set(const char *dev_name, const char *name, struct pinctrl_dev *pctldev; int pin; - pctldev = get_pinctrl_dev_from_dev(NULL, dev_name); + pctldev = get_pinctrl_dev_from_devname(dev_name); if (!pctldev) return -EINVAL; @@ -118,7 +118,7 @@ int pin_config_group_get(const char *dev_name, const char *pin_group, const struct pinconf_ops *ops; int selector; - pctldev = get_pinctrl_dev_from_dev(NULL, dev_name); + pctldev = get_pinctrl_dev_from_devname(dev_name); if (!pctldev) return -EINVAL; ops = pctldev->desc->confops; @@ -151,7 +151,7 @@ int pin_config_group_set(const char *dev_name, const char *pin_group, int ret; int i; - pctldev = get_pinctrl_dev_from_dev(NULL, dev_name); + pctldev = get_pinctrl_dev_from_devname(dev_name); if (!pctldev) return -EINVAL; ops = pctldev->desc->confops; diff --git a/drivers/pinctrl/pinmux.c b/drivers/pinctrl/pinmux.c index 7c3193f7a044..1311f1d22002 100644 --- a/drivers/pinctrl/pinmux.c +++ b/drivers/pinctrl/pinmux.c @@ -354,7 +354,7 @@ int __init pinmux_register_mappings(struct pinmux_map const *maps, return -EINVAL; } - if (!maps[i].ctrl_dev && !maps[i].ctrl_dev_name) { + if (!maps[i].ctrl_dev_name) { pr_err("failed to register map %s (%d): no pin control device given\n", maps[i].name, i); return -EINVAL; @@ -366,7 +366,7 @@ int __init pinmux_register_mappings(struct pinmux_map const *maps, return -EINVAL; } - if (!maps[i].dev && !maps[i].dev_name) + if (!maps[i].dev_name) pr_debug("add system map %s function %s with no device\n", maps[i].name, maps[i].function); @@ -721,20 +721,12 @@ struct pinmux *pinmux_get(struct device *dev, const char *name) /* * First, try to find the pctldev given in the map */ - pctldev = get_pinctrl_dev_from_dev(map->ctrl_dev, - map->ctrl_dev_name); + pctldev = get_pinctrl_dev_from_devname(map->ctrl_dev_name); if (!pctldev) { - const char *devname = NULL; - - if (map->ctrl_dev) - devname = dev_name(map->ctrl_dev); - else if (map->ctrl_dev_name) - devname = map->ctrl_dev_name; - pr_warning("could not find a pinctrl device for pinmux function %s, fishy, they shall all have one\n", map->function); pr_warning("given pinctrl device name: %s", - devname ? devname : "UNDEFINED"); + map->ctrl_dev_name); /* Continue to check the other mappings anyway... */ continue; @@ -925,7 +917,7 @@ static int pinmux_hog_map(struct pinctrl_dev *pctldev, struct pinmux *pmx; int ret; - if (map->dev || map->dev_name) { + if (map->dev_name) { /* * TODO: the day we have device tree support, we can * traverse the device tree and hog to specific device nodes @@ -996,9 +988,8 @@ int pinmux_hog_maps(struct pinctrl_dev *pctldev) if (!map->hog_on_boot) continue; - if ((map->ctrl_dev == dev) || - (map->ctrl_dev_name && - !strcmp(map->ctrl_dev_name, devname))) { + if (map->ctrl_dev_name && + !strcmp(map->ctrl_dev_name, devname)) { /* OK time to hog! */ ret = pinmux_hog_map(pctldev, map); if (ret) @@ -1156,14 +1147,12 @@ static int pinmux_maps_show(struct seq_file *s, void *what) struct pinmux_map const *map = &pinmux_maps[i]; seq_printf(s, "%s:\n", map->name); - if (map->dev || map->dev_name) + if (map->dev_name) seq_printf(s, " device: %s\n", - map->dev ? dev_name(map->dev) : map->dev_name); else seq_printf(s, " SYSTEM MUX\n"); seq_printf(s, " controlling device %s\n", - map->ctrl_dev ? dev_name(map->ctrl_dev) : map->ctrl_dev_name); seq_printf(s, " function: %s\n", map->function); seq_printf(s, " group: %s\n", map->group ? map->group : diff --git a/include/linux/pinctrl/machine.h b/include/linux/pinctrl/machine.h index d0aecb7f6fb9..f8593fdc6466 100644 --- a/include/linux/pinctrl/machine.h +++ b/include/linux/pinctrl/machine.h @@ -17,22 +17,16 @@ * @name: the name of this specific map entry for the particular machine. * This is the second parameter passed to pinmux_get() when you want * to have several mappings to the same device - * @ctrl_dev: the pin control device to be used by this mapping, may be NULL - * if you provide .ctrl_dev_name instead (this is more common) * @ctrl_dev_name: the name of the device controlling this specific mapping, - * the name must be the same as in your struct device*, may be NULL if - * you provide .ctrl_dev instead + * the name must be the same as in your struct device* * @function: a function in the driver to use for this mapping, the driver * will lookup the function referenced by this ID on the specified * pin control device * @group: sometimes a function can map to different pin groups, so this * selects a certain specific pin group to activate for the function, if * left as NULL, the first applicable group will be used - * @dev: the device using this specific mapping, may be NULL if you provide - * .dev_name instead (this is more common) * @dev_name: the name of the device using this specific mapping, the name - * must be the same as in your struct device*, may be NULL if you - * provide .dev instead + * must be the same as in your struct device* * @hog_on_boot: if this is set to true, the pin control subsystem will itself * hog the mappings as the pinmux device drivers are attached, so this is * typically used with system maps (mux mappings without an assigned @@ -42,11 +36,9 @@ */ struct pinmux_map { const char *name; - struct device *ctrl_dev; const char *ctrl_dev_name; const char *function; const char *group; - struct device *dev; const char *dev_name; bool hog_on_boot; }; -- cgit v1.2.3 From b89529a10c954f14191367355da2a6053c49abb9 Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Thu, 12 Jan 2012 19:40:34 +0100 Subject: Input: Use accessor for MT values The current MT accessor function does not distinguish between the MT values and the slot specification event. Add an accessor function for the values only, and use it where appropriate. Signed-off-by: Henrik Rydberg --- drivers/input/input.c | 2 +- include/linux/input/mt.h | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/drivers/input/input.c b/drivers/input/input.c index 1f78c957a75a..8921c6180c51 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -180,7 +180,7 @@ static int input_handle_abs_event(struct input_dev *dev, return INPUT_IGNORE_EVENT; } - is_mt_event = code >= ABS_MT_FIRST && code <= ABS_MT_LAST; + is_mt_event = input_is_mt_value(code); if (!is_mt_event) { pold = &dev->absinfo[code].value; diff --git a/include/linux/input/mt.h b/include/linux/input/mt.h index 318bb82325a6..f86737586e19 100644 --- a/include/linux/input/mt.h +++ b/include/linux/input/mt.h @@ -48,10 +48,14 @@ static inline void input_mt_slot(struct input_dev *dev, int slot) input_event(dev, EV_ABS, ABS_MT_SLOT, slot); } +static inline bool input_is_mt_value(int axis) +{ + return axis >= ABS_MT_FIRST && axis <= ABS_MT_LAST; +} + static inline bool input_is_mt_axis(int axis) { - return axis == ABS_MT_SLOT || - (axis >= ABS_MT_FIRST && axis <= ABS_MT_LAST); + return axis == ABS_MT_SLOT || input_is_mt_value(axis); } void input_mt_report_slot_state(struct input_dev *dev, -- cgit v1.2.3 From a80b83b7b8456e9b475346c2e01d7e210883208c Mon Sep 17 00:00:00 2001 From: John Stultz Date: Fri, 3 Feb 2012 00:19:07 -0800 Subject: Input: add infrastructure for selecting clockid for event time stamps As noted by Arve and others, since wall time can jump backwards, it is difficult to use for input because one cannot determine if one event occurred before another or for how long a key was pressed. However, the timestamp field is part of the kernel ABI, and cannot be changed without possibly breaking existing users. This patch adds a new IOCTL that allows a clockid to be set in the evdev_client struct that will specify which time base to use for event timestamps (ie: CLOCK_MONOTONIC instead of CLOCK_REALTIME). For now we only support CLOCK_MONOTONIC and CLOCK_REALTIME, but in the future we could support other clockids if appropriate. The default remains CLOCK_REALTIME, so we don't change the ABI. Signed-off-by: John Stultz Reviewed-by: Daniel Kurtz Signed-off-by: Dmitry Torokhov --- drivers/input/evdev.c | 25 +++++++++++++++++++++---- include/linux/input.h | 2 ++ kernel/time/timekeeping.c | 2 ++ 3 files changed, 25 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index 76457d50bc34..c17409742999 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -46,6 +46,7 @@ struct evdev_client { struct fasync_struct *fasync; struct evdev *evdev; struct list_head node; + int clkid; unsigned int bufsize; struct input_event buffer[]; }; @@ -54,8 +55,12 @@ static struct evdev *evdev_table[EVDEV_MINORS]; static DEFINE_MUTEX(evdev_table_mutex); static void evdev_pass_event(struct evdev_client *client, - struct input_event *event) + struct input_event *event, + ktime_t mono, ktime_t real) { + event->time = ktime_to_timeval(client->clkid == CLOCK_MONOTONIC ? + mono : real); + /* Interrupts are disabled, just acquire the lock. */ spin_lock(&client->buffer_lock); @@ -94,8 +99,11 @@ static void evdev_event(struct input_handle *handle, struct evdev *evdev = handle->private; struct evdev_client *client; struct input_event event; + ktime_t time_mono, time_real; + + time_mono = ktime_get(); + time_real = ktime_sub(time_mono, ktime_get_monotonic_offset()); - do_gettimeofday(&event.time); event.type = type; event.code = code; event.value = value; @@ -103,11 +111,12 @@ static void evdev_event(struct input_handle *handle, rcu_read_lock(); client = rcu_dereference(evdev->grab); + if (client) - evdev_pass_event(client, &event); + evdev_pass_event(client, &event, time_mono, time_real); else list_for_each_entry_rcu(client, &evdev->client_list, node) - evdev_pass_event(client, &event); + evdev_pass_event(client, &event, time_mono, time_real); rcu_read_unlock(); @@ -685,6 +694,14 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd, else return evdev_ungrab(evdev, client); + case EVIOCSCLOCKID: + if (copy_from_user(&i, p, sizeof(unsigned int))) + return -EFAULT; + if (i != CLOCK_MONOTONIC && i != CLOCK_REALTIME) + return -EINVAL; + client->clkid = i; + return 0; + case EVIOCGKEYCODE: return evdev_handle_get_keycode(dev, p); diff --git a/include/linux/input.h b/include/linux/input.h index 3862e32c4eeb..177261ea6f52 100644 --- a/include/linux/input.h +++ b/include/linux/input.h @@ -129,6 +129,8 @@ struct input_keymap_entry { #define EVIOCGRAB _IOW('E', 0x90, int) /* Grab/Release device */ +#define EVIOCSCLOCKID _IOW('E', 0xa0, int) /* Set clockid to be used for timestamps */ + /* * Device properties and quirks */ diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index 2b021b0e8507..169479994755 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -1140,6 +1140,8 @@ ktime_t ktime_get_monotonic_offset(void) } while (read_seqretry(&xtime_lock, seq)); return timespec_to_ktime(wtom); } +EXPORT_SYMBOL_GPL(ktime_get_monotonic_offset); + /** * xtime_update() - advances the timekeeping infrastructure -- cgit v1.2.3 From 30816ac0495cb4f33fc8d748f64ac3cc880cb3c1 Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 20 Jan 2012 22:51:07 +0000 Subject: MFD: mcp-core: sanitize host creation/removal host_unregister() gives us no chance between removing the device and the mcp data structure being freed to access the data inbetween, which drivers may need to do if they need to iounmap() pointers in their private data structures. Therefore, re-jig the interfaces, which are now, on creation: mcp = mcp_host_alloc() if (mcp) { ret = mcp_host_add(mcp, data); if (!ret) mcp_host_free(mcp); } and on removal: mcp_host_del(mcp); ... access mcp ... mcp_host_free(mcp); The free does the final put_device() on the struct device as one would expect. Acked-by: Jochen Friedrich Signed-off-by: Russell King --- drivers/mfd/mcp-core.c | 19 +++++++++++++------ drivers/mfd/mcp-sa11x0.c | 6 ++++-- include/linux/mfd/mcp.h | 5 +++-- 3 files changed, 20 insertions(+), 10 deletions(-) (limited to 'include/linux') diff --git a/drivers/mfd/mcp-core.c b/drivers/mfd/mcp-core.c index 86cc3f7841cd..cc7643177841 100644 --- a/drivers/mfd/mcp-core.c +++ b/drivers/mfd/mcp-core.c @@ -208,6 +208,7 @@ struct mcp *mcp_host_alloc(struct device *parent, size_t size) mcp = kzalloc(sizeof(struct mcp) + size, GFP_KERNEL); if (mcp) { spin_lock_init(&mcp->lock); + device_initialize(&mcp->attached_device); mcp->attached_device.parent = parent; mcp->attached_device.bus = &mcp_bus_type; mcp->attached_device.dma_mask = parent->dma_mask; @@ -217,18 +218,24 @@ struct mcp *mcp_host_alloc(struct device *parent, size_t size) } EXPORT_SYMBOL(mcp_host_alloc); -int mcp_host_register(struct mcp *mcp) +int mcp_host_add(struct mcp *mcp) { dev_set_name(&mcp->attached_device, "mcp0"); - return device_register(&mcp->attached_device); + return device_add(&mcp->attached_device); } -EXPORT_SYMBOL(mcp_host_register); +EXPORT_SYMBOL(mcp_host_add); -void mcp_host_unregister(struct mcp *mcp) +void mcp_host_del(struct mcp *mcp) { - device_unregister(&mcp->attached_device); + device_del(&mcp->attached_device); } -EXPORT_SYMBOL(mcp_host_unregister); +EXPORT_SYMBOL(mcp_host_del); + +void mcp_host_free(struct mcp *mcp) +{ + put_device(&mcp->attached_device); +} +EXPORT_SYMBOL(mcp_host_free); int mcp_driver_register(struct mcp_driver *mcpdrv) { diff --git a/drivers/mfd/mcp-sa11x0.c b/drivers/mfd/mcp-sa11x0.c index 02c53a0766c4..33cadc0ec121 100644 --- a/drivers/mfd/mcp-sa11x0.c +++ b/drivers/mfd/mcp-sa11x0.c @@ -195,10 +195,11 @@ static int mcp_sa11x0_probe(struct platform_device *pdev) mcp->rw_timeout = (64 * 3 * 1000000 + mcp->sclk_rate - 1) / mcp->sclk_rate; - ret = mcp_host_register(mcp); + ret = mcp_host_add(mcp); if (ret == 0) goto out; + mcp_host_free(mcp); release: release_mem_region(0x80060000, 0x60); platform_set_drvdata(pdev, NULL); @@ -212,7 +213,8 @@ static int mcp_sa11x0_remove(struct platform_device *dev) struct mcp *mcp = platform_get_drvdata(dev); platform_set_drvdata(dev, NULL); - mcp_host_unregister(mcp); + mcp_host_del(mcp); + mcp_host_free(mcp); release_mem_region(0x80060000, 0x60); return 0; diff --git a/include/linux/mfd/mcp.h b/include/linux/mfd/mcp.h index f88c1cc0cb0f..79a6b13ba20c 100644 --- a/include/linux/mfd/mcp.h +++ b/include/linux/mfd/mcp.h @@ -47,8 +47,9 @@ void mcp_disable(struct mcp *); #define mcp_get_sclk_rate(mcp) ((mcp)->sclk_rate) struct mcp *mcp_host_alloc(struct device *, size_t); -int mcp_host_register(struct mcp *); -void mcp_host_unregister(struct mcp *); +int mcp_host_add(struct mcp *); +void mcp_host_del(struct mcp *); +void mcp_host_free(struct mcp *); struct mcp_driver { struct device_driver drv; -- cgit v1.2.3 From 7658e7f9a8122b0678e4b4280308560aa5444bd5 Mon Sep 17 00:00:00 2001 From: Russell King Date: Thu, 12 Jan 2012 19:04:43 +0000 Subject: MFD: mcp-sa11x0: remove DMA initializers and variables The dma_device_t variables are only ever written to by mcp-sa11x0 and never read. As the old SA11x0 DMA support will be removed, remove these so that it no longer depends on the old SA11x0 DMA definitions. Acked-by: Jochen Friedrich Signed-off-by: Russell King --- drivers/mfd/mcp-core.c | 1 - drivers/mfd/mcp-sa11x0.c | 5 ----- drivers/mfd/ucb1x00-assabet.c | 3 --- drivers/mfd/ucb1x00-core.c | 1 - drivers/mfd/ucb1x00-ts.c | 1 - include/linux/mfd/mcp.h | 6 ------ 6 files changed, 17 deletions(-) (limited to 'include/linux') diff --git a/drivers/mfd/mcp-core.c b/drivers/mfd/mcp-core.c index cc7643177841..280a4f8a7876 100644 --- a/drivers/mfd/mcp-core.c +++ b/drivers/mfd/mcp-core.c @@ -19,7 +19,6 @@ #include #include -#include #include diff --git a/drivers/mfd/mcp-sa11x0.c b/drivers/mfd/mcp-sa11x0.c index 33cadc0ec121..d2ebc641b014 100644 --- a/drivers/mfd/mcp-sa11x0.c +++ b/drivers/mfd/mcp-sa11x0.c @@ -20,7 +20,6 @@ #include #include -#include #include #include #include @@ -158,10 +157,6 @@ static int mcp_sa11x0_probe(struct platform_device *pdev) mcp->owner = THIS_MODULE; mcp->ops = &mcp_sa11x0; mcp->sclk_rate = data->sclk_rate; - mcp->dma_audio_rd = DMA_Ser4MCP0Rd; - mcp->dma_audio_wr = DMA_Ser4MCP0Wr; - mcp->dma_telco_rd = DMA_Ser4MCP1Rd; - mcp->dma_telco_wr = DMA_Ser4MCP1Wr; mcp->gpio_base = data->gpio_base; platform_set_drvdata(pdev, mcp); diff --git a/drivers/mfd/ucb1x00-assabet.c b/drivers/mfd/ucb1x00-assabet.c index cea9da60850d..b7be613cb503 100644 --- a/drivers/mfd/ucb1x00-assabet.c +++ b/drivers/mfd/ucb1x00-assabet.c @@ -16,9 +16,6 @@ #include #include -#include - - #define UCB1X00_ATTR(name,input)\ static ssize_t name##_show(struct device *dev, struct device_attribute *attr, \ char *buf) \ diff --git a/drivers/mfd/ucb1x00-core.c b/drivers/mfd/ucb1x00-core.c index febc90cdef7e..f2fb4205467c 100644 --- a/drivers/mfd/ucb1x00-core.c +++ b/drivers/mfd/ucb1x00-core.c @@ -29,7 +29,6 @@ #include #include -#include #include static DEFINE_MUTEX(ucb1x00_mutex); diff --git a/drivers/mfd/ucb1x00-ts.c b/drivers/mfd/ucb1x00-ts.c index 63a3cbdfa3f3..ec6ffb6e287d 100644 --- a/drivers/mfd/ucb1x00-ts.c +++ b/drivers/mfd/ucb1x00-ts.c @@ -32,7 +32,6 @@ #include #include -#include #include #include diff --git a/include/linux/mfd/mcp.h b/include/linux/mfd/mcp.h index 79a6b13ba20c..dfe7e517ad9b 100644 --- a/include/linux/mfd/mcp.h +++ b/include/linux/mfd/mcp.h @@ -10,8 +10,6 @@ #ifndef MCP_H #define MCP_H -#include - struct mcp_ops; struct mcp { @@ -21,10 +19,6 @@ struct mcp { int use_count; unsigned int sclk_rate; unsigned int rw_timeout; - dma_device_t dma_audio_rd; - dma_device_t dma_audio_wr; - dma_device_t dma_telco_rd; - dma_device_t dma_telco_wr; struct device attached_device; int gpio_base; }; -- cgit v1.2.3 From 883381246c5ac2c29b849fe619f55fa5961ee76d Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Mon, 6 Feb 2012 15:18:48 -0500 Subject: SUNRPC: Change the default limit to the number of TCP slots Since the scheme of limiting the number of TCP slots to whatever will fit in the current TCP window seems to be working well (Andy reports getting within 20% of the 'iperf' send performance on a 10GigE link) we should just let that be the default mode of operation. Users may still set their own limits using the tcp_max_slot_table_entries parameter if they need to. Signed-off-by: Trond Myklebust --- include/linux/nfs_fs_sb.h | 2 +- include/linux/sunrpc/xprt.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h index c23469308b8e..9e101c1b81cf 100644 --- a/include/linux/nfs_fs_sb.h +++ b/include/linux/nfs_fs_sb.h @@ -190,7 +190,7 @@ struct nfs_server { /* maximum number of slots to use */ -#define NFS4_MAX_SLOT_TABLE RPC_MAX_SLOT_TABLE +#define NFS4_MAX_SLOT_TABLE (128U) #if defined(CONFIG_NFS_V4) diff --git a/include/linux/sunrpc/xprt.h b/include/linux/sunrpc/xprt.h index 15518a152ac3..b033f366d5f6 100644 --- a/include/linux/sunrpc/xprt.h +++ b/include/linux/sunrpc/xprt.h @@ -21,8 +21,8 @@ #define RPC_MIN_SLOT_TABLE (2U) #define RPC_DEF_SLOT_TABLE (16U) -#define RPC_MAX_SLOT_TABLE (128U) #define RPC_MAX_SLOT_TABLE_LIMIT (65536U) +#define RPC_MAX_SLOT_TABLE RPC_MAX_SLOT_TABLE_LIMIT /* * This describes a timeout strategy -- cgit v1.2.3 From e6499c6f4b5f56a16f8b8ef60529c1da28b13aea Mon Sep 17 00:00:00 2001 From: Bryan Schumaker Date: Thu, 26 Jan 2012 16:54:23 -0500 Subject: NFS: Fall back on old idmapper if request_key() fails This patch removes the CONFIG_NFS_USE_NEW_IDMAPPER compile option. First, the idmapper will attempt to map the id using /sbin/request-key and nfsidmap. If this fails (if /etc/request-key.conf is not configured properly) then the idmapper will call the legacy code to perform the mapping. I left a comment stating where the legacy code begins to make it easier for somebody to remove in the future. Signed-off-by: Bryan Schumaker Signed-off-by: Trond Myklebust --- fs/nfs/Kconfig | 11 ------ fs/nfs/idmap.c | 91 +++++++++++++++++++---------------------------- fs/nfs/sysctl.c | 2 -- include/linux/nfs_idmap.h | 15 -------- 4 files changed, 37 insertions(+), 82 deletions(-) (limited to 'include/linux') diff --git a/fs/nfs/Kconfig b/fs/nfs/Kconfig index dbcd82126aed..021d2cf6938a 100644 --- a/fs/nfs/Kconfig +++ b/fs/nfs/Kconfig @@ -132,14 +132,3 @@ config NFS_USE_KERNEL_DNS select DNS_RESOLVER select KEYS default y - -config NFS_USE_NEW_IDMAPPER - bool "Use the new idmapper upcall routine" - depends on NFS_V4 && KEYS - help - Say Y here if you want NFS to use the new idmapper upcall functions. - You will need /sbin/request-key (usually provided by the keyutils - package). For details, read - . - - If you are unsure, say N. diff --git a/fs/nfs/idmap.c b/fs/nfs/idmap.c index 62264e0b1ddb..e0ecd5a7e19a 100644 --- a/fs/nfs/idmap.c +++ b/fs/nfs/idmap.c @@ -142,8 +142,6 @@ static int nfs_map_numeric_to_string(__u32 id, char *buf, size_t buflen) return snprintf(buf, buflen, "%u", id); } -#ifdef CONFIG_NFS_USE_NEW_IDMAPPER - #include #include #include @@ -169,7 +167,7 @@ struct key_type key_type_id_resolver = { .read = user_read, }; -int nfs_idmap_init(void) +static int nfs_idmap_init_keyring(void) { struct cred *cred; struct key *keyring; @@ -211,7 +209,7 @@ failed_put_cred: return ret; } -void nfs_idmap_quit(void) +static void nfs_idmap_quit_keyring(void) { key_revoke(id_resolver_cache->thread_keyring); unregister_key_type(&key_type_id_resolver); @@ -328,43 +326,7 @@ static int nfs_idmap_lookup_id(const char *name, size_t namelen, return ret; } -int nfs_map_name_to_uid(const struct nfs_server *server, const char *name, size_t namelen, __u32 *uid) -{ - if (nfs_map_string_to_numeric(name, namelen, uid)) - return 0; - return nfs_idmap_lookup_id(name, namelen, "uid", uid); -} - -int nfs_map_group_to_gid(const struct nfs_server *server, const char *name, size_t namelen, __u32 *gid) -{ - if (nfs_map_string_to_numeric(name, namelen, gid)) - return 0; - return nfs_idmap_lookup_id(name, namelen, "gid", gid); -} - -int nfs_map_uid_to_name(const struct nfs_server *server, __u32 uid, char *buf, size_t buflen) -{ - int ret = -EINVAL; - - if (!(server->caps & NFS_CAP_UIDGID_NOMAP)) - ret = nfs_idmap_lookup_name(uid, "user", buf, buflen); - if (ret < 0) - ret = nfs_map_numeric_to_string(uid, buf, buflen); - return ret; -} -int nfs_map_gid_to_group(const struct nfs_server *server, __u32 gid, char *buf, size_t buflen) -{ - int ret = -EINVAL; - - if (!(server->caps & NFS_CAP_UIDGID_NOMAP)) - ret = nfs_idmap_lookup_name(gid, "group", buf, buflen); - if (ret < 0) - ret = nfs_map_numeric_to_string(gid, buf, buflen); - return ret; -} - -#else /* CONFIG_NFS_USE_NEW_IDMAPPER not defined */ - +/* idmap classic begins here */ #include #include #include @@ -600,12 +562,21 @@ static struct notifier_block nfs_idmap_block = { int nfs_idmap_init(void) { - return rpc_pipefs_notifier_register(&nfs_idmap_block); + int ret; + ret = nfs_idmap_init_keyring(); + if (ret != 0) + goto out; + ret = rpc_pipefs_notifier_register(&nfs_idmap_block); + if (ret != 0) + nfs_idmap_quit_keyring(); +out: + return ret; } void nfs_idmap_quit(void) { rpc_pipefs_notifier_unregister(&nfs_idmap_block); + nfs_idmap_quit_keyring(); } /* @@ -930,19 +901,27 @@ static unsigned int fnvhash32(const void *buf, size_t buflen) int nfs_map_name_to_uid(const struct nfs_server *server, const char *name, size_t namelen, __u32 *uid) { struct idmap *idmap = server->nfs_client->cl_idmap; + int ret = -EINVAL; if (nfs_map_string_to_numeric(name, namelen, uid)) return 0; - return nfs_idmap_id(idmap, &idmap->idmap_user_hash, name, namelen, uid); + ret = nfs_idmap_lookup_id(name, namelen, "uid", uid); + if (ret < 0) + ret = nfs_idmap_id(idmap, &idmap->idmap_user_hash, name, namelen, uid); + return ret; } -int nfs_map_group_to_gid(const struct nfs_server *server, const char *name, size_t namelen, __u32 *uid) +int nfs_map_group_to_gid(const struct nfs_server *server, const char *name, size_t namelen, __u32 *gid) { struct idmap *idmap = server->nfs_client->cl_idmap; + int ret = -EINVAL; - if (nfs_map_string_to_numeric(name, namelen, uid)) + if (nfs_map_string_to_numeric(name, namelen, gid)) return 0; - return nfs_idmap_id(idmap, &idmap->idmap_group_hash, name, namelen, uid); + ret = nfs_idmap_lookup_id(name, namelen, "gid", gid); + if (ret < 0) + ret = nfs_idmap_id(idmap, &idmap->idmap_group_hash, name, namelen, gid); + return ret; } int nfs_map_uid_to_name(const struct nfs_server *server, __u32 uid, char *buf, size_t buflen) @@ -950,22 +929,26 @@ int nfs_map_uid_to_name(const struct nfs_server *server, __u32 uid, char *buf, s struct idmap *idmap = server->nfs_client->cl_idmap; int ret = -EINVAL; - if (!(server->caps & NFS_CAP_UIDGID_NOMAP)) - ret = nfs_idmap_name(idmap, &idmap->idmap_user_hash, uid, buf); + if (!(server->caps & NFS_CAP_UIDGID_NOMAP)) { + ret = nfs_idmap_lookup_name(uid, "user", buf, buflen); + if (ret < 0) + ret = nfs_idmap_name(idmap, &idmap->idmap_user_hash, uid, buf); + } if (ret < 0) ret = nfs_map_numeric_to_string(uid, buf, buflen); return ret; } -int nfs_map_gid_to_group(const struct nfs_server *server, __u32 uid, char *buf, size_t buflen) +int nfs_map_gid_to_group(const struct nfs_server *server, __u32 gid, char *buf, size_t buflen) { struct idmap *idmap = server->nfs_client->cl_idmap; int ret = -EINVAL; - if (!(server->caps & NFS_CAP_UIDGID_NOMAP)) - ret = nfs_idmap_name(idmap, &idmap->idmap_group_hash, uid, buf); + if (!(server->caps & NFS_CAP_UIDGID_NOMAP)) { + ret = nfs_idmap_lookup_name(gid, "group", buf, buflen); + if (ret < 0) + ret = nfs_idmap_name(idmap, &idmap->idmap_group_hash, gid, buf); + } if (ret < 0) - ret = nfs_map_numeric_to_string(uid, buf, buflen); + ret = nfs_map_numeric_to_string(gid, buf, buflen); return ret; } - -#endif /* CONFIG_NFS_USE_NEW_IDMAPPER */ diff --git a/fs/nfs/sysctl.c b/fs/nfs/sysctl.c index 978aaeb8a093..ad4d2e787b20 100644 --- a/fs/nfs/sysctl.c +++ b/fs/nfs/sysctl.c @@ -32,7 +32,6 @@ static ctl_table nfs_cb_sysctls[] = { .extra1 = (int *)&nfs_set_port_min, .extra2 = (int *)&nfs_set_port_max, }, -#ifndef CONFIG_NFS_USE_NEW_IDMAPPER { .procname = "idmap_cache_timeout", .data = &nfs_idmap_cache_timeout, @@ -40,7 +39,6 @@ static ctl_table nfs_cb_sysctls[] = { .mode = 0644, .proc_handler = proc_dointvec_jiffies, }, -#endif /* CONFIG_NFS_USE_NEW_IDMAPPER */ #endif { .procname = "nfs_mountpoint_timeout", diff --git a/include/linux/nfs_idmap.h b/include/linux/nfs_idmap.h index 3c9eeb7da646..7eed2012d288 100644 --- a/include/linux/nfs_idmap.h +++ b/include/linux/nfs_idmap.h @@ -82,24 +82,9 @@ static inline void nfs_idmap_quit(void) {} #endif -#ifdef CONFIG_NFS_USE_NEW_IDMAPPER - -static inline int nfs_idmap_new(struct nfs_client *clp) -{ - return 0; -} - -static inline void nfs_idmap_delete(struct nfs_client *clp) -{ -} - -#else /* CONFIG_NFS_USE_NEW_IDMAPPER not set */ - int nfs_idmap_new(struct nfs_client *); void nfs_idmap_delete(struct nfs_client *); -#endif /* CONFIG_NFS_USE_NEW_IDMAPPER */ - void nfs_fattr_init_names(struct nfs_fattr *fattr, struct nfs4_string *owner_name, struct nfs4_string *group_name); -- cgit v1.2.3 From 17711dbf4788ded84470941ff63a7029f73ca654 Mon Sep 17 00:00:00 2001 From: Olof Johansson Date: Sun, 16 Oct 2011 16:54:51 -0700 Subject: ARM: tegra: emc: convert tegra2_emc to a platform driver This is the first step in making it device-tree aware and get rid of the in-kernel EMC tables (of which there are none in mainline, thankfully). Changes since v3: * moved to devm_request_and_ioremap() in probe() Changes since v2: * D'oh -- missed a couple of variables that were added, never used and then later removed in a later patch. Changes since v1: * Fixed messed up indentation * Removed code that should be gone (was added here and removed later in series) Signed-off-by: Olof Johansson Acked-by: Stephen Warren --- arch/arm/mach-tegra/tegra2_emc.c | 92 ++++++++++++++++++++++++--------- arch/arm/mach-tegra/tegra2_emc.h | 11 ++-- include/linux/platform_data/tegra_emc.h | 34 ++++++++++++ 3 files changed, 107 insertions(+), 30 deletions(-) create mode 100644 include/linux/platform_data/tegra_emc.h (limited to 'include/linux') diff --git a/arch/arm/mach-tegra/tegra2_emc.c b/arch/arm/mach-tegra/tegra2_emc.c index 0f7ae6e90b55..e6229bbbb83a 100644 --- a/arch/arm/mach-tegra/tegra2_emc.c +++ b/arch/arm/mach-tegra/tegra2_emc.c @@ -16,10 +16,13 @@ */ #include +#include #include #include #include #include +#include +#include #include @@ -32,18 +35,17 @@ static bool emc_enable; #endif module_param(emc_enable, bool, 0644); -static void __iomem *emc = IO_ADDRESS(TEGRA_EMC_BASE); -static const struct tegra_emc_table *tegra_emc_table; -static int tegra_emc_table_size; +static struct platform_device *emc_pdev; +static void __iomem *emc_regbase; static inline void emc_writel(u32 val, unsigned long addr) { - writel(val, emc + addr); + writel(val, emc_regbase + addr); } static inline u32 emc_readl(unsigned long addr) { - return readl(emc + addr); + return readl(emc_regbase + addr); } static const unsigned long emc_reg_addr[TEGRA_EMC_NUM_REGS] = { @@ -98,15 +100,15 @@ static const unsigned long emc_reg_addr[TEGRA_EMC_NUM_REGS] = { /* Select the closest EMC rate that is higher than the requested rate */ long tegra_emc_round_rate(unsigned long rate) { + struct tegra_emc_pdata *pdata; int i; int best = -1; unsigned long distance = ULONG_MAX; - if (!tegra_emc_table) + if (!emc_pdev) return -EINVAL; - if (!emc_enable) - return -EINVAL; + pdata = emc_pdev->dev.platform_data; pr_debug("%s: %lu\n", __func__, rate); @@ -116,10 +118,10 @@ long tegra_emc_round_rate(unsigned long rate) */ rate = rate / 2 / 1000; - for (i = 0; i < tegra_emc_table_size; i++) { - if (tegra_emc_table[i].rate >= rate && - (tegra_emc_table[i].rate - rate) < distance) { - distance = tegra_emc_table[i].rate - rate; + for (i = 0; i < pdata->num_tables; i++) { + if (pdata->tables[i].rate >= rate && + (pdata->tables[i].rate - rate) < distance) { + distance = pdata->tables[i].rate - rate; best = i; } } @@ -127,9 +129,9 @@ long tegra_emc_round_rate(unsigned long rate) if (best < 0) return -EINVAL; - pr_debug("%s: using %lu\n", __func__, tegra_emc_table[best].rate); + pr_debug("%s: using %lu\n", __func__, pdata->tables[best].rate); - return tegra_emc_table[best].rate * 2 * 1000; + return pdata->tables[best].rate * 2 * 1000; } /* @@ -142,37 +144,81 @@ long tegra_emc_round_rate(unsigned long rate) */ int tegra_emc_set_rate(unsigned long rate) { + struct tegra_emc_pdata *pdata; int i; int j; - if (!tegra_emc_table) + if (!emc_pdev) return -EINVAL; + pdata = emc_pdev->dev.platform_data; + /* * The EMC clock rate is twice the bus rate, and the bus rate is * measured in kHz */ rate = rate / 2 / 1000; - for (i = 0; i < tegra_emc_table_size; i++) - if (tegra_emc_table[i].rate == rate) + for (i = 0; i < pdata->num_tables; i++) + if (pdata->tables[i].rate == rate) break; - if (i >= tegra_emc_table_size) + if (i >= pdata->num_tables) return -EINVAL; pr_debug("%s: setting to %lu\n", __func__, rate); for (j = 0; j < TEGRA_EMC_NUM_REGS; j++) - emc_writel(tegra_emc_table[i].regs[j], emc_reg_addr[j]); + emc_writel(pdata->tables[i].regs[j], emc_reg_addr[j]); + + emc_readl(pdata->tables[i].regs[TEGRA_EMC_NUM_REGS - 1]); + + return 0; +} + +static int __devinit tegra_emc_probe(struct platform_device *pdev) +{ + struct tegra_emc_pdata *pdata; + struct resource *res; + + if (!emc_enable) { + dev_err(&pdev->dev, "disabled per module parameter\n"); + return -ENODEV; + } + + pdata = pdev->dev.platform_data; - emc_readl(tegra_emc_table[i].regs[TEGRA_EMC_NUM_REGS - 1]); + if (!pdata) { + dev_err(&pdev->dev, "missing platform data\n"); + return -ENXIO; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "missing register base\n"); + return -ENOMEM; + } + + emc_regbase = devm_request_and_ioremap(&pdev->dev, res); + if (!emc_regbase) { + dev_err(&pdev->dev, "failed to remap registers\n"); + return -ENOMEM; + } + emc_pdev = pdev; return 0; } -void tegra_init_emc(const struct tegra_emc_table *table, int table_size) +static struct platform_driver tegra_emc_driver = { + .driver = { + .name = "tegra-emc", + .owner = THIS_MODULE, + }, + .probe = tegra_emc_probe, +}; + +static int __init tegra_emc_init(void) { - tegra_emc_table = table; - tegra_emc_table_size = table_size; + return platform_driver_register(&tegra_emc_driver); } +device_initcall(tegra_emc_init); diff --git a/arch/arm/mach-tegra/tegra2_emc.h b/arch/arm/mach-tegra/tegra2_emc.h index 19f08cb31603..f61409b54cb7 100644 --- a/arch/arm/mach-tegra/tegra2_emc.h +++ b/arch/arm/mach-tegra/tegra2_emc.h @@ -15,13 +15,10 @@ * */ -#define TEGRA_EMC_NUM_REGS 46 - -struct tegra_emc_table { - unsigned long rate; - u32 regs[TEGRA_EMC_NUM_REGS]; -}; +#ifndef __MACH_TEGRA_TEGRA2_EMC_H_ +#define __MACH_TEGRA_TEGRA2_EMC_H int tegra_emc_set_rate(unsigned long rate); long tegra_emc_round_rate(unsigned long rate); -void tegra_init_emc(const struct tegra_emc_table *table, int table_size); + +#endif diff --git a/include/linux/platform_data/tegra_emc.h b/include/linux/platform_data/tegra_emc.h new file mode 100644 index 000000000000..df67505e98f8 --- /dev/null +++ b/include/linux/platform_data/tegra_emc.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2011 Google, Inc. + * + * Author: + * Colin Cross + * Olof Johansson + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#ifndef __TEGRA_EMC_H_ +#define __TEGRA_EMC_H_ + +#define TEGRA_EMC_NUM_REGS 46 + +struct tegra_emc_table { + unsigned long rate; + u32 regs[TEGRA_EMC_NUM_REGS]; +}; + +struct tegra_emc_pdata { + int num_tables; + struct tegra_emc_table *tables; +}; + +#endif -- cgit v1.2.3 From 400e64df6b237eb36b127efd72000a2794f9eec1 Mon Sep 17 00:00:00 2001 From: Ohad Ben-Cohen Date: Thu, 20 Oct 2011 16:52:46 +0200 Subject: remoteproc: add framework for controlling remote processors Modern SoCs typically employ a central symmetric multiprocessing (SMP) application processor running Linux, with several other asymmetric multiprocessing (AMP) heterogeneous processors running different instances of operating system, whether Linux or any other flavor of real-time OS. Booting a remote processor in an AMP configuration typically involves: - Loading a firmware which contains the OS image - Allocating and providing it required system resources (e.g. memory) - Programming an IOMMU (when relevant) - Powering on the device This patch introduces a generic framework that allows drivers to do that. In the future, this framework will also include runtime power management and error recovery. Based on (but now quite far from) work done by Fernando Guzman Lugo . ELF loader was written by Mark Grosen , based on msm's Peripheral Image Loader (PIL) by Stephen Boyd . Designed with Brian Swetland . Signed-off-by: Ohad Ben-Cohen Acked-by: Grant Likely Cc: Brian Swetland Cc: Arnd Bergmann Cc: Tony Lindgren Cc: Russell King Cc: Rusty Russell Cc: Andrew Morton Cc: Greg KH Cc: Stephen Boyd --- Documentation/remoteproc.txt | 324 +++++++ MAINTAINERS | 7 + drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/remoteproc/Kconfig | 3 + drivers/remoteproc/Makefile | 6 + drivers/remoteproc/remoteproc_core.c | 1410 ++++++++++++++++++++++++++++++ drivers/remoteproc/remoteproc_internal.h | 44 + include/linux/remoteproc.h | 265 ++++++ 9 files changed, 2062 insertions(+) create mode 100644 Documentation/remoteproc.txt create mode 100644 drivers/remoteproc/Kconfig create mode 100644 drivers/remoteproc/Makefile create mode 100644 drivers/remoteproc/remoteproc_core.c create mode 100644 drivers/remoteproc/remoteproc_internal.h create mode 100644 include/linux/remoteproc.h (limited to 'include/linux') diff --git a/Documentation/remoteproc.txt b/Documentation/remoteproc.txt new file mode 100644 index 000000000000..23ff7349ffe7 --- /dev/null +++ b/Documentation/remoteproc.txt @@ -0,0 +1,324 @@ +Remote Processor Framework + +1. Introduction + +Modern SoCs typically have heterogeneous remote processor devices in asymmetric +multiprocessing (AMP) configurations, which may be running different instances +of operating system, whether it's Linux or any other flavor of real-time OS. + +OMAP4, for example, has dual Cortex-A9, dual Cortex-M3 and a C64x+ DSP. +In a typical configuration, the dual cortex-A9 is running Linux in a SMP +configuration, and each of the other three cores (two M3 cores and a DSP) +is running its own instance of RTOS in an AMP configuration. + +The remoteproc framework allows different platforms/architectures to +control (power on, load firmware, power off) those remote processors while +abstracting the hardware differences, so the entire driver doesn't need to be +duplicated. In addition, this framework also adds rpmsg virtio devices +for remote processors that supports this kind of communication. This way, +platform-specific remoteproc drivers only need to provide a few low-level +handlers, and then all rpmsg drivers will then just work +(for more information about the virtio-based rpmsg bus and its drivers, +please read Documentation/rpmsg.txt). + +2. User API + + int rproc_boot(struct rproc *rproc) + - Boot a remote processor (i.e. load its firmware, power it on, ...). + If the remote processor is already powered on, this function immediately + returns (successfully). + Returns 0 on success, and an appropriate error value otherwise. + Note: to use this function you should already have a valid rproc + handle. There are several ways to achieve that cleanly (devres, pdata, + the way remoteproc_rpmsg.c does this, or, if this becomes prevalent, we + might also consider using dev_archdata for this). See also + rproc_get_by_name() below. + + void rproc_shutdown(struct rproc *rproc) + - Power off a remote processor (previously booted with rproc_boot()). + In case @rproc is still being used by an additional user(s), then + this function will just decrement the power refcount and exit, + without really powering off the device. + Every call to rproc_boot() must (eventually) be accompanied by a call + to rproc_shutdown(). Calling rproc_shutdown() redundantly is a bug. + Notes: + - we're not decrementing the rproc's refcount, only the power refcount. + which means that the @rproc handle stays valid even after + rproc_shutdown() returns, and users can still use it with a subsequent + rproc_boot(), if needed. + - don't call rproc_shutdown() to unroll rproc_get_by_name(), exactly + because rproc_shutdown() _does not_ decrement the refcount of @rproc. + To decrement the refcount of @rproc, use rproc_put() (but _only_ if + you acquired @rproc using rproc_get_by_name()). + + struct rproc *rproc_get_by_name(const char *name) + - Find an rproc handle using the remote processor's name, and then + boot it. If it's already powered on, then just immediately return + (successfully). Returns the rproc handle on success, and NULL on failure. + This function increments the remote processor's refcount, so always + use rproc_put() to decrement it back once rproc isn't needed anymore. + Note: currently rproc_get_by_name() and rproc_put() are not used anymore + by the rpmsg bus and its drivers. We need to scrutinize the use cases + that still need them, and see if we can migrate them to use the non + name-based boot/shutdown interface. + + void rproc_put(struct rproc *rproc) + - Decrement @rproc's power refcount and shut it down if it reaches zero + (essentially by just calling rproc_shutdown), and then decrement @rproc's + validity refcount too. + After this function returns, @rproc may _not_ be used anymore, and its + handle should be considered invalid. + This function should be called _iff_ the @rproc handle was grabbed by + calling rproc_get_by_name(). + +3. Typical usage + +#include + +/* in case we were given a valid 'rproc' handle */ +int dummy_rproc_example(struct rproc *my_rproc) +{ + int ret; + + /* let's power on and boot our remote processor */ + ret = rproc_boot(my_rproc); + if (ret) { + /* + * something went wrong. handle it and leave. + */ + } + + /* + * our remote processor is now powered on... give it some work + */ + + /* let's shut it down now */ + rproc_shutdown(my_rproc); +} + +4. API for implementors + + struct rproc *rproc_alloc(struct device *dev, const char *name, + const struct rproc_ops *ops, + const char *firmware, int len) + - Allocate a new remote processor handle, but don't register + it yet. Required parameters are the underlying device, the + name of this remote processor, platform-specific ops handlers, + the name of the firmware to boot this rproc with, and the + length of private data needed by the allocating rproc driver (in bytes). + + This function should be used by rproc implementations during + initialization of the remote processor. + After creating an rproc handle using this function, and when ready, + implementations should then call rproc_register() to complete + the registration of the remote processor. + On success, the new rproc is returned, and on failure, NULL. + + Note: _never_ directly deallocate @rproc, even if it was not registered + yet. Instead, if you just need to unroll rproc_alloc(), use rproc_free(). + + void rproc_free(struct rproc *rproc) + - Free an rproc handle that was allocated by rproc_alloc. + This function should _only_ be used if @rproc was only allocated, + but not registered yet. + If @rproc was already successfully registered (by calling + rproc_register()), then use rproc_unregister() instead. + + int rproc_register(struct rproc *rproc) + - Register @rproc with the remoteproc framework, after it has been + allocated with rproc_alloc(). + This is called by the platform-specific rproc implementation, whenever + a new remote processor device is probed. + Returns 0 on success and an appropriate error code otherwise. + Note: this function initiates an asynchronous firmware loading + context, which will look for virtio devices supported by the rproc's + firmware. + If found, those virtio devices will be created and added, so as a result + of registering this remote processor, additional virtio drivers might get + probed. + Currently, though, we only support a single RPMSG virtio vdev per remote + processor. + + int rproc_unregister(struct rproc *rproc) + - Unregister a remote processor, and decrement its refcount. + If its refcount drops to zero, then @rproc will be freed. If not, + it will be freed later once the last reference is dropped. + + This function should be called when the platform specific rproc + implementation decides to remove the rproc device. it should + _only_ be called if a previous invocation of rproc_register() + has completed successfully. + + After rproc_unregister() returns, @rproc is _not_ valid anymore and + it shouldn't be used. More specifically, don't call rproc_free() + or try to directly free @rproc after rproc_unregister() returns; + none of these are needed, and calling them is a bug. + + Returns 0 on success and -EINVAL if @rproc isn't valid. + +5. Implementation callbacks + +These callbacks should be provided by platform-specific remoteproc +drivers: + +/** + * struct rproc_ops - platform-specific device handlers + * @start: power on the device and boot it + * @stop: power off the device + * @kick: kick a virtqueue (virtqueue id given as a parameter) + */ +struct rproc_ops { + int (*start)(struct rproc *rproc); + int (*stop)(struct rproc *rproc); + void (*kick)(struct rproc *rproc, int vqid); +}; + +Every remoteproc implementation should at least provide the ->start and ->stop +handlers. If rpmsg functionality is also desired, then the ->kick handler +should be provided as well. + +The ->start() handler takes an rproc handle and should then power on the +device and boot it (use rproc->priv to access platform-specific private data). +The boot address, in case needed, can be found in rproc->bootaddr (remoteproc +core puts there the ELF entry point). +On success, 0 should be returned, and on failure, an appropriate error code. + +The ->stop() handler takes an rproc handle and powers the device down. +On success, 0 is returned, and on failure, an appropriate error code. + +The ->kick() handler takes an rproc handle, and an index of a virtqueue +where new message was placed in. Implementations should interrupt the remote +processor and let it know it has pending messages. Notifying remote processors +the exact virtqueue index to look in is optional: it is easy (and not +too expensive) to go through the existing virtqueues and look for new buffers +in the used rings. + +6. Binary Firmware Structure + +At this point remoteproc only supports ELF32 firmware binaries. However, +it is quite expected that other platforms/devices which we'd want to +support with this framework will be based on different binary formats. + +When those use cases show up, we will have to decouple the binary format +from the framework core, so we can support several binary formats without +duplicating common code. + +When the firmware is parsed, its various segments are loaded to memory +according to the specified device address (might be a physical address +if the remote processor is accessing memory directly). + +In addition to the standard ELF segments, most remote processors would +also include a special section which we call "the resource table". + +The resource table contains system resources that the remote processor +requires before it should be powered on, such as allocation of physically +contiguous memory, or iommu mapping of certain on-chip peripherals. +Remotecore will only power up the device after all the resource table's +requirement are met. + +In addition to system resources, the resource table may also contain +resource entries that publish the existence of supported features +or configurations by the remote processor, such as trace buffers and +supported virtio devices (and their configurations). + +Currently the resource table is just an array of: + +/** + * struct fw_resource - describes an entry from the resource section + * @type: resource type + * @id: index number of the resource + * @da: device address of the resource + * @pa: physical address of the resource + * @len: size, in bytes, of the resource + * @flags: properties of the resource, e.g. iommu protection required + * @reserved: must be 0 atm + * @name: name of resource + */ +struct fw_resource { + u32 type; + u32 id; + u64 da; + u64 pa; + u32 len; + u32 flags; + u8 reserved[16]; + u8 name[48]; +} __packed; + +Some resources entries are mere announcements, where the host is informed +of specific remoteproc configuration. Other entries require the host to +do something (e.g. reserve a requested resource) and possibly also reply +by overwriting a member inside 'struct fw_resource' with info about the +allocated resource. + +Different resource entries use different members of this struct, +with different meanings. This is pretty limiting and error-prone, +so the plan is to move to variable-length TLV-based resource entries, +where each resource will begin with a type and length fields, followed by +its own specific structure. + +Here are the resource types that are currently being used: + +/** + * enum fw_resource_type - types of resource entries + * + * @RSC_CARVEOUT: request for allocation of a physically contiguous + * memory region. + * @RSC_DEVMEM: request to iommu_map a memory-based peripheral. + * @RSC_TRACE: announces the availability of a trace buffer into which + * the remote processor will be writing logs. In this case, + * 'da' indicates the device address where logs are written to, + * and 'len' is the size of the trace buffer. + * @RSC_VRING: request for allocation of a virtio vring (address should + * be indicated in 'da', and 'len' should contain the number + * of buffers supported by the vring). + * @RSC_VIRTIO_DEV: announces support for a virtio device, and serves as + * the virtio header. 'da' contains the virtio device + * features, 'pa' holds the virtio guest features (host + * will write them here after they're negotiated), 'len' + * holds the virtio status, and 'flags' holds the virtio + * device id (currently only VIRTIO_ID_RPMSG is supported). + */ +enum fw_resource_type { + RSC_CARVEOUT = 0, + RSC_DEVMEM = 1, + RSC_TRACE = 2, + RSC_VRING = 3, + RSC_VIRTIO_DEV = 4, + RSC_VIRTIO_CFG = 5, +}; + +Most of the resource entries share the basic idea of address/length +negotiation with the host: the firmware usually asks for memory +of size 'len' bytes, and the host needs to allocate it and provide +the device/physical address (when relevant) in 'da'/'pa' respectively. + +If the firmware is compiled with hard coded device addresses, and +can't handle dynamically allocated 'da' values, then the 'da' field +will contain the expected device addresses (today we actually only support +this scheme, as there aren't yet any use cases for dynamically allocated +device addresses). + +We also expect that platform-specific resource entries will show up +at some point. When that happens, we could easily add a new RSC_PLAFORM +type, and hand those resources to the platform-specific rproc driver to handle. + +7. Virtio and remoteproc + +The firmware should provide remoteproc information about virtio devices +that it supports, and their configurations: a RSC_VIRTIO_DEV resource entry +should specify the virtio device id, and subsequent RSC_VRING resource entries +should indicate the vring size (i.e. how many buffers do they support) and +where should they be mapped (i.e. which device address). Note: the alignment +between the consumer and producer parts of the vring is assumed to be 4096. + +At this point we only support a single virtio rpmsg device per remote +processor, but the plan is to remove this limitation. In addition, once we +move to TLV-based resource table, the plan is to have a single RSC_VIRTIO +entry per supported virtio device, which will include the virtio header, +the vrings information and the virtio config space. + +Of course, RSC_VIRTIO resource entries are only good enough for static +allocation of virtio devices. Dynamic allocations will also be made possible +using the rpmsg bus (similar to how we already do dynamic allocations of +rpmsg channels; read more about it in rpmsg.txt). diff --git a/MAINTAINERS b/MAINTAINERS index 89b70df91f4f..b611319dc77a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5548,6 +5548,13 @@ S: Supported F: drivers/base/regmap/ F: include/linux/regmap.h +REMOTE PROCESSOR (REMOTEPROC) SUBSYSTEM +M: Ohad Ben-Cohen +S: Maintained +F: drivers/remoteproc/ +F: Documentation/remoteproc.txt +F: include/linux/remoteproc.txt + RFKILL M: Johannes Berg L: linux-wireless@vger.kernel.org diff --git a/drivers/Kconfig b/drivers/Kconfig index 5afe5d1f199b..27b34bf41d4c 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -132,6 +132,8 @@ source "drivers/clocksource/Kconfig" source "drivers/iommu/Kconfig" +source "drivers/remoteproc/Kconfig" + source "drivers/virt/Kconfig" source "drivers/devfreq/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index c07be024b962..f1019b714f27 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -126,6 +126,7 @@ obj-y += clk/ obj-$(CONFIG_HWSPINLOCK) += hwspinlock/ obj-$(CONFIG_NFC) += nfc/ obj-$(CONFIG_IOMMU_SUPPORT) += iommu/ +obj-$(CONFIG_REMOTEPROC) += remoteproc/ # Virtualization drivers obj-$(CONFIG_VIRT_DRIVERS) += virt/ diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig new file mode 100644 index 000000000000..b250b15c0686 --- /dev/null +++ b/drivers/remoteproc/Kconfig @@ -0,0 +1,3 @@ +# REMOTEPROC gets selected by whoever wants it +config REMOTEPROC + tristate diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile new file mode 100644 index 000000000000..2a5fd7992f5a --- /dev/null +++ b/drivers/remoteproc/Makefile @@ -0,0 +1,6 @@ +# +# Generic framework for controlling remote processors +# + +obj-$(CONFIG_REMOTEPROC) += remoteproc.o +remoteproc-y := remoteproc_core.o diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c new file mode 100644 index 000000000000..ad93d7d4ecbb --- /dev/null +++ b/drivers/remoteproc/remoteproc_core.c @@ -0,0 +1,1410 @@ +/* + * Remote Processor Framework + * + * Copyright (C) 2011 Texas Instruments, Inc. + * Copyright (C) 2011 Google, Inc. + * + * Ohad Ben-Cohen + * Brian Swetland + * Mark Grosen + * Fernando Guzman Lugo + * Suman Anna + * Robert Tivy + * Armando Uribe De Leon + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * 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. + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "remoteproc_internal.h" + +static void klist_rproc_get(struct klist_node *n); +static void klist_rproc_put(struct klist_node *n); + +/* + * klist of the available remote processors. + * + * We need this in order to support name-based lookups (needed by the + * rproc_get_by_name()). + * + * That said, we don't use rproc_get_by_name() anymore within the rpmsg + * framework. The use cases that do require its existence should be + * scrutinized, and hopefully migrated to rproc_boot() using device-based + * binding. + * + * If/when this materializes, we could drop the klist (and the by_name + * API). + */ +static DEFINE_KLIST(rprocs, klist_rproc_get, klist_rproc_put); + +typedef int (*rproc_handle_resources_t)(struct rproc *rproc, + struct fw_resource *rsc, int len); + +/* + * This is the IOMMU fault handler we register with the IOMMU API + * (when relevant; not all remote processors access memory through + * an IOMMU). + * + * IOMMU core will invoke this handler whenever the remote processor + * will try to access an unmapped device address. + * + * Currently this is mostly a stub, but it will be later used to trigger + * the recovery of the remote processor. + */ +static int rproc_iommu_fault(struct iommu_domain *domain, struct device *dev, + unsigned long iova, int flags) +{ + dev_err(dev, "iommu fault: da 0x%lx flags 0x%x\n", iova, flags); + + /* + * Let the iommu core know we're not really handling this fault; + * we just plan to use this as a recovery trigger. + */ + return -ENOSYS; +} + +static int rproc_enable_iommu(struct rproc *rproc) +{ + struct iommu_domain *domain; + struct device *dev = rproc->dev; + int ret; + + /* + * We currently use iommu_present() to decide if an IOMMU + * setup is needed. + * + * This works for simple cases, but will easily fail with + * platforms that do have an IOMMU, but not for this specific + * rproc. + * + * This will be easily solved by introducing hw capabilities + * that will be set by the remoteproc driver. + */ + if (!iommu_present(dev->bus)) { + dev_err(dev, "iommu not found\n"); + return -ENODEV; + } + + domain = iommu_domain_alloc(dev->bus); + if (!domain) { + dev_err(dev, "can't alloc iommu domain\n"); + return -ENOMEM; + } + + iommu_set_fault_handler(domain, rproc_iommu_fault); + + ret = iommu_attach_device(domain, dev); + if (ret) { + dev_err(dev, "can't attach iommu device: %d\n", ret); + goto free_domain; + } + + rproc->domain = domain; + + return 0; + +free_domain: + iommu_domain_free(domain); + return ret; +} + +static void rproc_disable_iommu(struct rproc *rproc) +{ + struct iommu_domain *domain = rproc->domain; + struct device *dev = rproc->dev; + + if (!domain) + return; + + iommu_detach_device(domain, dev); + iommu_domain_free(domain); + + return; +} + +/* + * Some remote processors will ask us to allocate them physically contiguous + * memory regions (which we call "carveouts"), and map them to specific + * device addresses (which are hardcoded in the firmware). + * + * They may then ask us to copy objects into specific device addresses (e.g. + * code/data sections) or expose us certain symbols in other device address + * (e.g. their trace buffer). + * + * This function is an internal helper with which we can go over the allocated + * carveouts and translate specific device address to kernel virtual addresses + * so we can access the referenced memory. + * + * Note: phys_to_virt(iommu_iova_to_phys(rproc->domain, da)) will work too, + * but only on kernel direct mapped RAM memory. Instead, we're just using + * here the output of the DMA API, which should be more correct. + */ +static void *rproc_da_to_va(struct rproc *rproc, u64 da, int len) +{ + struct rproc_mem_entry *carveout; + void *ptr = NULL; + + list_for_each_entry(carveout, &rproc->carveouts, node) { + int offset = da - carveout->da; + + /* try next carveout if da is too small */ + if (offset < 0) + continue; + + /* try next carveout if da is too large */ + if (offset + len > carveout->len) + continue; + + ptr = carveout->va + offset; + + break; + } + + return ptr; +} + +/** + * rproc_load_segments() - load firmware segments to memory + * @rproc: remote processor which will be booted using these fw segments + * @elf_data: the content of the ELF firmware image + * + * This function loads the firmware segments to memory, where the remote + * processor expects them. + * + * Some remote processors will expect their code and data to be placed + * in specific device addresses, and can't have them dynamically assigned. + * + * We currently support only those kind of remote processors, and expect + * the program header's paddr member to contain those addresses. We then go + * through the physically contiguous "carveout" memory regions which we + * allocated (and mapped) earlier on behalf of the remote processor, + * and "translate" device address to kernel addresses, so we can copy the + * segments where they are expected. + * + * Currently we only support remote processors that required carveout + * allocations and got them mapped onto their iommus. Some processors + * might be different: they might not have iommus, and would prefer to + * directly allocate memory for every segment/resource. This is not yet + * supported, though. + */ +static int rproc_load_segments(struct rproc *rproc, const u8 *elf_data) +{ + struct device *dev = rproc->dev; + struct elf32_hdr *ehdr; + struct elf32_phdr *phdr; + int i, ret = 0; + + ehdr = (struct elf32_hdr *)elf_data; + phdr = (struct elf32_phdr *)(elf_data + ehdr->e_phoff); + + /* go through the available ELF segments */ + for (i = 0; i < ehdr->e_phnum; i++, phdr++) { + u32 da = phdr->p_paddr; + u32 memsz = phdr->p_memsz; + u32 filesz = phdr->p_filesz; + void *ptr; + + if (phdr->p_type != PT_LOAD) + continue; + + dev_dbg(dev, "phdr: type %d da 0x%x memsz 0x%x filesz 0x%x\n", + phdr->p_type, da, memsz, filesz); + + if (filesz > memsz) { + dev_err(dev, "bad phdr filesz 0x%x memsz 0x%x\n", + filesz, memsz); + ret = -EINVAL; + break; + } + + /* grab the kernel address for this device address */ + ptr = rproc_da_to_va(rproc, da, memsz); + if (!ptr) { + dev_err(dev, "bad phdr da 0x%x mem 0x%x\n", da, memsz); + ret = -EINVAL; + break; + } + + /* put the segment where the remote processor expects it */ + if (phdr->p_filesz) + memcpy(ptr, elf_data + phdr->p_offset, filesz); + + /* + * Zero out remaining memory for this segment. + * + * This isn't strictly required since dma_alloc_coherent already + * did this for us. albeit harmless, we may consider removing + * this. + */ + if (memsz > filesz) + memset(ptr + filesz, 0, memsz - filesz); + } + + return ret; +} + +/** + * rproc_handle_virtio_hdr() - handle a virtio header resource + * @rproc: the remote processor + * @rsc: the resource descriptor + * + * The existence of this virtio hdr resource entry means that the firmware + * of this @rproc supports this virtio device. + * + * Currently we support only a single virtio device of type VIRTIO_ID_RPMSG, + * but the plan is to remove this limitation and support any number + * of virtio devices (and of any type). We'll also add support for dynamically + * adding (and removing) virtio devices over the rpmsg bus, but small + * firmwares that doesn't want to get involved with rpmsg will be able + * to simple use the resource table for this. + * + * At this point this virtio header entry is rather simple: it just + * announces the virtio device id and the supported virtio device features. + * The plan though is to extend this to include the vring information and + * the virtio config space, too (but first, some resource table overhaul + * is needed: move from fixed-sized to variable-length TLV entries). + * + * For now, the 'flags' member of the resource entry contains the virtio + * device id, the 'da' member contains the device features, and 'pa' is + * where we need to store the guest features once negotiation completes. + * As usual, the 'id' member of this resource contains the index of this + * resource type (i.e. is this the first virtio hdr entry, the 2nd, ...). + * + * Returns 0 on success, or an appropriate error code otherwise + */ +static int rproc_handle_virtio_hdr(struct rproc *rproc, struct fw_resource *rsc) +{ + struct rproc_vdev *rvdev; + + /* we only support VIRTIO_ID_RPMSG devices for now */ + if (rsc->flags != VIRTIO_ID_RPMSG) { + dev_warn(rproc->dev, "unsupported vdev: %d\n", rsc->flags); + return -EINVAL; + } + + /* we only support a single vdev per rproc for now */ + if (rsc->id || rproc->rvdev) { + dev_warn(rproc->dev, "redundant vdev entry: %s\n", rsc->name); + return -EINVAL; + } + + rvdev = kzalloc(sizeof(struct rproc_vdev), GFP_KERNEL); + if (!rvdev) + return -ENOMEM; + + /* remember the device features */ + rvdev->dfeatures = rsc->da; + + rproc->rvdev = rvdev; + rvdev->rproc = rproc; + + return 0; +} + +/** + * rproc_handle_vring() - handle a vring fw resource + * @rproc: the remote processor + * @rsc: the vring resource descriptor + * + * This resource entry requires allocation of non-cacheable memory + * for a virtio vring. Currently we only support two vrings per remote + * processor, required for the virtio rpmsg device. + * + * The 'len' member of @rsc should contain the number of buffers this vring + * support and 'da' should either contain the device address where + * the remote processor is expecting the vring, or indicate that + * dynamically allocation of the vring's device address is supported. + * + * Note: 'da' is currently not handled. This will be revised when the generic + * iommu-based DMA API will arrive, or a dynanic & non-iommu use case show + * up. Meanwhile, statically-addressed iommu-based images should use + * RSC_DEVMEM resource entries to map their require 'da' to the physical + * address of their base CMA region. + * + * Returns 0 on success, or an appropriate error code otherwise + */ +static int rproc_handle_vring(struct rproc *rproc, struct fw_resource *rsc) +{ + struct device *dev = rproc->dev; + struct rproc_vdev *rvdev = rproc->rvdev; + dma_addr_t dma; + int size, id = rsc->id; + void *va; + + /* no vdev is in place ? */ + if (!rvdev) { + dev_err(dev, "vring requested without a virtio dev entry\n"); + return -EINVAL; + } + + /* the firmware must provide the expected queue size */ + if (!rsc->len) { + dev_err(dev, "missing expected queue size\n"); + return -EINVAL; + } + + /* we currently support two vrings per rproc (for rx and tx) */ + if (id >= ARRAY_SIZE(rvdev->vring)) { + dev_err(dev, "%s: invalid vring id %d\n", rsc->name, id); + return -EINVAL; + } + + /* have we already allocated this vring id ? */ + if (rvdev->vring[id].len) { + dev_err(dev, "%s: duplicated id %d\n", rsc->name, id); + return -EINVAL; + } + + /* actual size of vring (in bytes) */ + size = PAGE_ALIGN(vring_size(rsc->len, AMP_VRING_ALIGN)); + + /* + * Allocate non-cacheable memory for the vring. In the future + * this call will also configure the IOMMU for us + */ + va = dma_alloc_coherent(dev, size, &dma, GFP_KERNEL); + if (!va) { + dev_err(dev, "dma_alloc_coherent failed\n"); + return -ENOMEM; + } + + dev_dbg(dev, "vring%d: va %p dma %x qsz %d ring size %x\n", id, va, + dma, rsc->len, size); + + rvdev->vring[id].len = rsc->len; + rvdev->vring[id].va = va; + rvdev->vring[id].dma = dma; + + return 0; +} + +/** + * rproc_handle_trace() - handle a shared trace buffer resource + * @rproc: the remote processor + * @rsc: the trace resource descriptor + * + * In case the remote processor dumps trace logs into memory, + * export it via debugfs. + * + * Currently, the 'da' member of @rsc should contain the device address + * where the remote processor is dumping the traces. Later we could also + * support dynamically allocating this address using the generic + * DMA API (but currently there isn't a use case for that). + * + * Returns 0 on success, or an appropriate error code otherwise + */ +static int rproc_handle_trace(struct rproc *rproc, struct fw_resource *rsc) +{ + struct rproc_mem_entry *trace; + struct device *dev = rproc->dev; + void *ptr; + char name[15]; + + /* what's the kernel address of this resource ? */ + ptr = rproc_da_to_va(rproc, rsc->da, rsc->len); + if (!ptr) { + dev_err(dev, "erroneous trace resource entry\n"); + return -EINVAL; + } + + trace = kzalloc(sizeof(*trace), GFP_KERNEL); + if (!trace) { + dev_err(dev, "kzalloc trace failed\n"); + return -ENOMEM; + } + + /* set the trace buffer dma properties */ + trace->len = rsc->len; + trace->va = ptr; + + /* make sure snprintf always null terminates, even if truncating */ + snprintf(name, sizeof(name), "trace%d", rproc->num_traces); + + /* create the debugfs entry */ + trace->priv = rproc_create_trace_file(name, rproc, trace); + if (!trace->priv) { + trace->va = NULL; + kfree(trace); + return -EINVAL; + } + + list_add_tail(&trace->node, &rproc->traces); + + rproc->num_traces++; + + dev_dbg(dev, "%s added: va %p, da 0x%llx, len 0x%x\n", name, ptr, + rsc->da, rsc->len); + + return 0; +} + +/** + * rproc_handle_devmem() - handle devmem resource entry + * @rproc: remote processor handle + * @rsc: the devmem resource entry + * + * Remote processors commonly need to access certain on-chip peripherals. + * + * Some of these remote processors access memory via an iommu device, + * and might require us to configure their iommu before they can access + * the on-chip peripherals they need. + * + * This resource entry is a request to map such a peripheral device. + * + * These devmem entries will contain the physical address of the device in + * the 'pa' member. If a specific device address is expected, then 'da' will + * contain it (currently this is the only use case supported). 'len' will + * contain the size of the physical region we need to map. + * + * Currently we just "trust" those devmem entries to contain valid physical + * addresses, but this is going to change: we want the implementations to + * tell us ranges of physical addresses the firmware is allowed to request, + * and not allow firmwares to request access to physical addresses that + * are outside those ranges. + */ +static int rproc_handle_devmem(struct rproc *rproc, struct fw_resource *rsc) +{ + struct rproc_mem_entry *mapping; + int ret; + + /* no point in handling this resource without a valid iommu domain */ + if (!rproc->domain) + return -EINVAL; + + mapping = kzalloc(sizeof(*mapping), GFP_KERNEL); + if (!mapping) { + dev_err(rproc->dev, "kzalloc mapping failed\n"); + return -ENOMEM; + } + + ret = iommu_map(rproc->domain, rsc->da, rsc->pa, rsc->len, rsc->flags); + if (ret) { + dev_err(rproc->dev, "failed to map devmem: %d\n", ret); + goto out; + } + + /* + * We'll need this info later when we'll want to unmap everything + * (e.g. on shutdown). + * + * We can't trust the remote processor not to change the resource + * table, so we must maintain this info independently. + */ + mapping->da = rsc->da; + mapping->len = rsc->len; + list_add_tail(&mapping->node, &rproc->mappings); + + dev_dbg(rproc->dev, "mapped devmem pa 0x%llx, da 0x%llx, len 0x%x\n", + rsc->pa, rsc->da, rsc->len); + + return 0; + +out: + kfree(mapping); + return ret; +} + +/** + * rproc_handle_carveout() - handle phys contig memory allocation requests + * @rproc: rproc handle + * @rsc: the resource entry + * + * This function will handle firmware requests for allocation of physically + * contiguous memory regions. + * + * These request entries should come first in the firmware's resource table, + * as other firmware entries might request placing other data objects inside + * these memory regions (e.g. data/code segments, trace resource entries, ...). + * + * Allocating memory this way helps utilizing the reserved physical memory + * (e.g. CMA) more efficiently, and also minimizes the number of TLB entries + * needed to map it (in case @rproc is using an IOMMU). Reducing the TLB + * pressure is important; it may have a substantial impact on performance. + */ +static int rproc_handle_carveout(struct rproc *rproc, struct fw_resource *rsc) +{ + struct rproc_mem_entry *carveout, *mapping; + struct device *dev = rproc->dev; + dma_addr_t dma; + void *va; + int ret; + + mapping = kzalloc(sizeof(*mapping), GFP_KERNEL); + if (!mapping) { + dev_err(dev, "kzalloc mapping failed\n"); + return -ENOMEM; + } + + carveout = kzalloc(sizeof(*carveout), GFP_KERNEL); + if (!carveout) { + dev_err(dev, "kzalloc carveout failed\n"); + ret = -ENOMEM; + goto free_mapping; + } + + va = dma_alloc_coherent(dev, rsc->len, &dma, GFP_KERNEL); + if (!va) { + dev_err(dev, "failed to dma alloc carveout: %d\n", rsc->len); + ret = -ENOMEM; + goto free_carv; + } + + dev_dbg(dev, "carveout va %p, dma %x, len 0x%x\n", va, dma, rsc->len); + + /* + * Ok, this is non-standard. + * + * Sometimes we can't rely on the generic iommu-based DMA API + * to dynamically allocate the device address and then set the IOMMU + * tables accordingly, because some remote processors might + * _require_ us to use hard coded device addresses that their + * firmware was compiled with. + * + * In this case, we must use the IOMMU API directly and map + * the memory to the device address as expected by the remote + * processor. + * + * Obviously such remote processor devices should not be configured + * to use the iommu-based DMA API: we expect 'dma' to contain the + * physical address in this case. + */ + if (rproc->domain) { + ret = iommu_map(rproc->domain, rsc->da, dma, rsc->len, + rsc->flags); + if (ret) { + dev_err(dev, "iommu_map failed: %d\n", ret); + goto dma_free; + } + + /* + * We'll need this info later when we'll want to unmap + * everything (e.g. on shutdown). + * + * We can't trust the remote processor not to change the + * resource table, so we must maintain this info independently. + */ + mapping->da = rsc->da; + mapping->len = rsc->len; + list_add_tail(&mapping->node, &rproc->mappings); + + dev_dbg(dev, "carveout mapped 0x%llx to 0x%x\n", rsc->da, dma); + + /* + * Some remote processors might need to know the pa + * even though they are behind an IOMMU. E.g., OMAP4's + * remote M3 processor needs this so it can control + * on-chip hardware accelerators that are not behind + * the IOMMU, and therefor must know the pa. + * + * Generally we don't want to expose physical addresses + * if we don't have to (remote processors are generally + * _not_ trusted), so we might want to do this only for + * remote processor that _must_ have this (e.g. OMAP4's + * dual M3 subsystem). + */ + rsc->pa = dma; + } + + carveout->va = va; + carveout->len = rsc->len; + carveout->dma = dma; + carveout->da = rsc->da; + + list_add_tail(&carveout->node, &rproc->carveouts); + + return 0; + +dma_free: + dma_free_coherent(dev, rsc->len, va, dma); +free_carv: + kfree(carveout); +free_mapping: + kfree(mapping); + return ret; +} + +/* handle firmware resource entries before booting the remote processor */ +static int +rproc_handle_boot_rsc(struct rproc *rproc, struct fw_resource *rsc, int len) +{ + struct device *dev = rproc->dev; + int ret = 0; + + while (len >= sizeof(*rsc)) { + dev_dbg(dev, "rsc: type %d, da 0x%llx, pa 0x%llx, len 0x%x, " + "id %d, name %s, flags %x\n", rsc->type, rsc->da, + rsc->pa, rsc->len, rsc->id, rsc->name, rsc->flags); + + switch (rsc->type) { + case RSC_CARVEOUT: + ret = rproc_handle_carveout(rproc, rsc); + break; + case RSC_DEVMEM: + ret = rproc_handle_devmem(rproc, rsc); + break; + case RSC_TRACE: + ret = rproc_handle_trace(rproc, rsc); + break; + case RSC_VRING: + ret = rproc_handle_vring(rproc, rsc); + break; + case RSC_VIRTIO_DEV: + /* this one is handled early upon registration */ + break; + default: + dev_warn(dev, "unsupported resource %d\n", rsc->type); + break; + } + + if (ret) + break; + + rsc++; + len -= sizeof(*rsc); + } + + return ret; +} + +/* handle firmware resource entries while registering the remote processor */ +static int +rproc_handle_virtio_rsc(struct rproc *rproc, struct fw_resource *rsc, int len) +{ + struct device *dev = rproc->dev; + int ret = 0; + + for (; len >= sizeof(*rsc); rsc++, len -= sizeof(*rsc)) + if (rsc->type == RSC_VIRTIO_DEV) { + dev_dbg(dev, "found vdev %d/%s features %llx\n", + rsc->flags, rsc->name, rsc->da); + ret = rproc_handle_virtio_hdr(rproc, rsc); + break; + } + + return ret; +} + +/** + * rproc_handle_resources() - find and handle the resource table + * @rproc: the rproc handle + * @elf_data: the content of the ELF firmware image + * @handler: function that should be used to handle the resource table + * + * This function finds the resource table inside the remote processor's + * firmware, and invoke a user-supplied handler with it (we have two + * possible handlers: one is invoked upon registration of @rproc, + * in order to register the supported virito devices, and the other is + * invoked when @rproc is actually booted). + * + * Currently this function fails if a resource table doesn't exist. + * This restriction will be removed when we'll start supporting remote + * processors that don't need a resource table. + */ +static int rproc_handle_resources(struct rproc *rproc, const u8 *elf_data, + rproc_handle_resources_t handler) + +{ + struct elf32_hdr *ehdr; + struct elf32_shdr *shdr; + const char *name_table; + int i, ret = -EINVAL; + + ehdr = (struct elf32_hdr *)elf_data; + shdr = (struct elf32_shdr *)(elf_data + ehdr->e_shoff); + name_table = elf_data + shdr[ehdr->e_shstrndx].sh_offset; + + /* look for the resource table and handle it */ + for (i = 0; i < ehdr->e_shnum; i++, shdr++) { + if (!strcmp(name_table + shdr->sh_name, ".resource_table")) { + struct fw_resource *table = (struct fw_resource *) + (elf_data + shdr->sh_offset); + + ret = handler(rproc, table, shdr->sh_size); + + break; + } + } + + return ret; +} + +/** + * rproc_resource_cleanup() - clean up and free all acquired resources + * @rproc: rproc handle + * + * This function will free all resources acquired for @rproc, and it + * is called when @rproc shuts down, or just failed booting. + */ +static void rproc_resource_cleanup(struct rproc *rproc) +{ + struct rproc_mem_entry *entry, *tmp; + struct device *dev = rproc->dev; + struct rproc_vdev *rvdev = rproc->rvdev; + int i; + + /* clean up debugfs trace entries */ + list_for_each_entry_safe(entry, tmp, &rproc->traces, node) { + rproc_remove_trace_file(entry->priv); + rproc->num_traces--; + list_del(&entry->node); + kfree(entry); + } + + /* free the coherent memory allocated for the vrings */ + for (i = 0; rvdev && i < ARRAY_SIZE(rvdev->vring); i++) { + int qsz = rvdev->vring[i].len; + void *va = rvdev->vring[i].va; + int dma = rvdev->vring[i].dma; + + /* virtqueue size is expressed in number of buffers supported */ + if (qsz) { + /* how many bytes does this vring really occupy ? */ + int size = PAGE_ALIGN(vring_size(qsz, AMP_VRING_ALIGN)); + + dma_free_coherent(rproc->dev, size, va, dma); + + rvdev->vring[i].len = 0; + } + } + + /* clean up carveout allocations */ + list_for_each_entry_safe(entry, tmp, &rproc->carveouts, node) { + dma_free_coherent(dev, entry->len, entry->va, entry->dma); + list_del(&entry->node); + kfree(entry); + } + + /* clean up iommu mapping entries */ + list_for_each_entry_safe(entry, tmp, &rproc->mappings, node) { + size_t unmapped; + + unmapped = iommu_unmap(rproc->domain, entry->da, entry->len); + if (unmapped != entry->len) { + /* nothing much to do besides complaining */ + dev_err(dev, "failed to unmap %u/%u\n", entry->len, + unmapped); + } + + list_del(&entry->node); + kfree(entry); + } +} + +/* make sure this fw image is sane */ +static int rproc_fw_sanity_check(struct rproc *rproc, const struct firmware *fw) +{ + const char *name = rproc->firmware; + struct device *dev = rproc->dev; + struct elf32_hdr *ehdr; + + if (!fw) { + dev_err(dev, "failed to load %s\n", name); + return -EINVAL; + } + + if (fw->size < sizeof(struct elf32_hdr)) { + dev_err(dev, "Image is too small\n"); + return -EINVAL; + } + + ehdr = (struct elf32_hdr *)fw->data; + + if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG)) { + dev_err(dev, "Image is corrupted (bad magic)\n"); + return -EINVAL; + } + + if (ehdr->e_phnum == 0) { + dev_err(dev, "No loadable segments\n"); + return -EINVAL; + } + + if (ehdr->e_phoff > fw->size) { + dev_err(dev, "Firmware size is too small\n"); + return -EINVAL; + } + + return 0; +} + +/* + * take a firmware and boot a remote processor with it. + */ +static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw) +{ + struct device *dev = rproc->dev; + const char *name = rproc->firmware; + struct elf32_hdr *ehdr; + int ret; + + ret = rproc_fw_sanity_check(rproc, fw); + if (ret) + return ret; + + ehdr = (struct elf32_hdr *)fw->data; + + dev_info(dev, "Booting fw image %s, size %d\n", name, fw->size); + + /* + * if enabling an IOMMU isn't relevant for this rproc, this is + * just a nop + */ + ret = rproc_enable_iommu(rproc); + if (ret) { + dev_err(dev, "can't enable iommu: %d\n", ret); + return ret; + } + + /* + * The ELF entry point is the rproc's boot addr (though this is not + * a configurable property of all remote processors: some will always + * boot at a specific hardcoded address). + */ + rproc->bootaddr = ehdr->e_entry; + + /* handle fw resources which are required to boot rproc */ + ret = rproc_handle_resources(rproc, fw->data, rproc_handle_boot_rsc); + if (ret) { + dev_err(dev, "Failed to process resources: %d\n", ret); + goto clean_up; + } + + /* load the ELF segments to memory */ + ret = rproc_load_segments(rproc, fw->data); + if (ret) { + dev_err(dev, "Failed to load program segments: %d\n", ret); + goto clean_up; + } + + /* power up the remote processor */ + ret = rproc->ops->start(rproc); + if (ret) { + dev_err(dev, "can't start rproc %s: %d\n", rproc->name, ret); + goto clean_up; + } + + rproc->state = RPROC_RUNNING; + + dev_info(dev, "remote processor %s is now up\n", rproc->name); + + return 0; + +clean_up: + rproc_resource_cleanup(rproc); + rproc_disable_iommu(rproc); + return ret; +} + +/* + * take a firmware and look for virtio devices to register. + * + * Note: this function is called asynchronously upon registration of the + * remote processor (so we must wait until it completes before we try + * to unregister the device. one other option is just to use kref here, + * that might be cleaner). + */ +static void rproc_fw_config_virtio(const struct firmware *fw, void *context) +{ + struct rproc *rproc = context; + struct device *dev = rproc->dev; + int ret; + + if (rproc_fw_sanity_check(rproc, fw) < 0) + goto out; + + /* does the fw supports any virtio devices ? */ + ret = rproc_handle_resources(rproc, fw->data, rproc_handle_virtio_rsc); + if (ret) { + dev_info(dev, "No fw virtio device was found\n"); + goto out; + } + + /* add the virtio device (currently only rpmsg vdevs are supported) */ + ret = rproc_add_rpmsg_vdev(rproc); + if (ret) + goto out; + +out: + if (fw) + release_firmware(fw); + /* allow rproc_unregister() contexts, if any, to proceed */ + complete_all(&rproc->firmware_loading_complete); +} + +/** + * rproc_boot() - boot a remote processor + * @rproc: handle of a remote processor + * + * Boot a remote processor (i.e. load its firmware, power it on, ...). + * + * If the remote processor is already powered on, this function immediately + * returns (successfully). + * + * Returns 0 on success, and an appropriate error value otherwise. + */ +int rproc_boot(struct rproc *rproc) +{ + const struct firmware *firmware_p; + struct device *dev; + int ret; + + if (!rproc) { + pr_err("invalid rproc handle\n"); + return -EINVAL; + } + + dev = rproc->dev; + + ret = mutex_lock_interruptible(&rproc->lock); + if (ret) { + dev_err(dev, "can't lock rproc %s: %d\n", rproc->name, ret); + return ret; + } + + /* loading a firmware is required */ + if (!rproc->firmware) { + dev_err(dev, "%s: no firmware to load\n", __func__); + ret = -EINVAL; + goto unlock_mutex; + } + + /* prevent underlying implementation from being removed */ + if (!try_module_get(dev->driver->owner)) { + dev_err(dev, "%s: can't get owner\n", __func__); + ret = -EINVAL; + goto unlock_mutex; + } + + /* skip the boot process if rproc is already powered up */ + if (atomic_inc_return(&rproc->power) > 1) { + ret = 0; + goto unlock_mutex; + } + + dev_info(dev, "powering up %s\n", rproc->name); + + /* load firmware */ + ret = request_firmware(&firmware_p, rproc->firmware, dev); + if (ret < 0) { + dev_err(dev, "request_firmware failed: %d\n", ret); + goto downref_rproc; + } + + ret = rproc_fw_boot(rproc, firmware_p); + + release_firmware(firmware_p); + +downref_rproc: + if (ret) { + module_put(dev->driver->owner); + atomic_dec(&rproc->power); + } +unlock_mutex: + mutex_unlock(&rproc->lock); + return ret; +} +EXPORT_SYMBOL(rproc_boot); + +/** + * rproc_shutdown() - power off the remote processor + * @rproc: the remote processor + * + * Power off a remote processor (previously booted with rproc_boot()). + * + * In case @rproc is still being used by an additional user(s), then + * this function will just decrement the power refcount and exit, + * without really powering off the device. + * + * Every call to rproc_boot() must (eventually) be accompanied by a call + * to rproc_shutdown(). Calling rproc_shutdown() redundantly is a bug. + * + * Notes: + * - we're not decrementing the rproc's refcount, only the power refcount. + * which means that the @rproc handle stays valid even after rproc_shutdown() + * returns, and users can still use it with a subsequent rproc_boot(), if + * needed. + * - don't call rproc_shutdown() to unroll rproc_get_by_name(), exactly + * because rproc_shutdown() _does not_ decrement the refcount of @rproc. + * To decrement the refcount of @rproc, use rproc_put() (but _only_ if + * you acquired @rproc using rproc_get_by_name()). + */ +void rproc_shutdown(struct rproc *rproc) +{ + struct device *dev = rproc->dev; + int ret; + + ret = mutex_lock_interruptible(&rproc->lock); + if (ret) { + dev_err(dev, "can't lock rproc %s: %d\n", rproc->name, ret); + return; + } + + /* if the remote proc is still needed, bail out */ + if (!atomic_dec_and_test(&rproc->power)) + goto out; + + /* power off the remote processor */ + ret = rproc->ops->stop(rproc); + if (ret) { + atomic_inc(&rproc->power); + dev_err(dev, "can't stop rproc: %d\n", ret); + goto out; + } + + /* clean up all acquired resources */ + rproc_resource_cleanup(rproc); + + rproc_disable_iommu(rproc); + + rproc->state = RPROC_OFFLINE; + + dev_info(dev, "stopped remote processor %s\n", rproc->name); + +out: + mutex_unlock(&rproc->lock); + if (!ret) + module_put(dev->driver->owner); +} +EXPORT_SYMBOL(rproc_shutdown); + +/** + * rproc_release() - completely deletes the existence of a remote processor + * @kref: the rproc's kref + * + * This function should _never_ be called directly. + * + * The only reasonable location to use it is as an argument when kref_put'ing + * @rproc's refcount. + * + * This way it will be called when no one holds a valid pointer to this @rproc + * anymore (and obviously after it is removed from the rprocs klist). + * + * Note: this function is not static because rproc_vdev_release() needs it when + * it decrements @rproc's refcount. + */ +void rproc_release(struct kref *kref) +{ + struct rproc *rproc = container_of(kref, struct rproc, refcount); + + dev_info(rproc->dev, "removing %s\n", rproc->name); + + rproc_delete_debug_dir(rproc); + + /* at this point no one holds a reference to rproc anymore */ + kfree(rproc); +} + +/* will be called when an rproc is added to the rprocs klist */ +static void klist_rproc_get(struct klist_node *n) +{ + struct rproc *rproc = container_of(n, struct rproc, node); + + kref_get(&rproc->refcount); +} + +/* will be called when an rproc is removed from the rprocs klist */ +static void klist_rproc_put(struct klist_node *n) +{ + struct rproc *rproc = container_of(n, struct rproc, node); + + kref_put(&rproc->refcount, rproc_release); +} + +static struct rproc *next_rproc(struct klist_iter *i) +{ + struct klist_node *n; + + n = klist_next(i); + if (!n) + return NULL; + + return container_of(n, struct rproc, node); +} + +/** + * rproc_get_by_name() - find a remote processor by name and boot it + * @name: name of the remote processor + * + * Finds an rproc handle using the remote processor's name, and then + * boot it. If it's already powered on, then just immediately return + * (successfully). + * + * Returns the rproc handle on success, and NULL on failure. + * + * This function increments the remote processor's refcount, so always + * use rproc_put() to decrement it back once rproc isn't needed anymore. + * + * Note: currently this function (and its counterpart rproc_put()) are not + * used anymore by the rpmsg subsystem. We need to scrutinize the use cases + * that still need them, and see if we can migrate them to use the non + * name-based boot/shutdown interface. + */ +struct rproc *rproc_get_by_name(const char *name) +{ + struct rproc *rproc; + struct klist_iter i; + int ret; + + /* find the remote processor, and upref its refcount */ + klist_iter_init(&rprocs, &i); + while ((rproc = next_rproc(&i)) != NULL) + if (!strcmp(rproc->name, name)) { + kref_get(&rproc->refcount); + break; + } + klist_iter_exit(&i); + + /* can't find this rproc ? */ + if (!rproc) { + pr_err("can't find remote processor %s\n", name); + return NULL; + } + + ret = rproc_boot(rproc); + if (ret < 0) { + kref_put(&rproc->refcount, rproc_release); + return NULL; + } + + return rproc; +} +EXPORT_SYMBOL(rproc_get_by_name); + +/** + * rproc_put() - decrement the refcount of a remote processor, and shut it down + * @rproc: the remote processor + * + * This function tries to shutdown @rproc, and it then decrements its + * refcount. + * + * After this function returns, @rproc may _not_ be used anymore, and its + * handle should be considered invalid. + * + * This function should be called _iff_ the @rproc handle was grabbed by + * calling rproc_get_by_name(). + */ +void rproc_put(struct rproc *rproc) +{ + /* try to power off the remote processor */ + rproc_shutdown(rproc); + + /* downref rproc's refcount */ + kref_put(&rproc->refcount, rproc_release); +} +EXPORT_SYMBOL(rproc_put); + +/** + * rproc_register() - register a remote processor + * @rproc: the remote processor handle to register + * + * Registers @rproc with the remoteproc framework, after it has been + * allocated with rproc_alloc(). + * + * This is called by the platform-specific rproc implementation, whenever + * a new remote processor device is probed. + * + * Returns 0 on success and an appropriate error code otherwise. + * + * Note: this function initiates an asynchronous firmware loading + * context, which will look for virtio devices supported by the rproc's + * firmware. + * + * If found, those virtio devices will be created and added, so as a result + * of registering this remote processor, additional virtio drivers will be + * probed. + * + * Currently, though, we only support a single RPMSG virtio vdev per remote + * processor. + */ +int rproc_register(struct rproc *rproc) +{ + struct device *dev = rproc->dev; + int ret = 0; + + /* expose to rproc_get_by_name users */ + klist_add_tail(&rproc->node, &rprocs); + + dev_info(rproc->dev, "%s is available\n", rproc->name); + + /* create debugfs entries */ + rproc_create_debug_dir(rproc); + + /* rproc_unregister() calls must wait until async loader completes */ + init_completion(&rproc->firmware_loading_complete); + + /* + * We must retrieve early virtio configuration info from + * the firmware (e.g. whether to register a virtio rpmsg device, + * what virtio features does it support, ...). + * + * We're initiating an asynchronous firmware loading, so we can + * be built-in kernel code, without hanging the boot process. + */ + ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + rproc->firmware, dev, GFP_KERNEL, + rproc, rproc_fw_config_virtio); + if (ret < 0) { + dev_err(dev, "request_firmware_nowait failed: %d\n", ret); + complete_all(&rproc->firmware_loading_complete); + klist_remove(&rproc->node); + } + + return ret; +} +EXPORT_SYMBOL(rproc_register); + +/** + * rproc_alloc() - allocate a remote processor handle + * @dev: the underlying device + * @name: name of this remote processor + * @ops: platform-specific handlers (mainly start/stop) + * @firmware: name of firmware file to load + * @len: length of private data needed by the rproc driver (in bytes) + * + * Allocates a new remote processor handle, but does not register + * it yet. + * + * This function should be used by rproc implementations during initialization + * of the remote processor. + * + * After creating an rproc handle using this function, and when ready, + * implementations should then call rproc_register() to complete + * the registration of the remote processor. + * + * On success the new rproc is returned, and on failure, NULL. + * + * Note: _never_ directly deallocate @rproc, even if it was not registered + * yet. Instead, if you just need to unroll rproc_alloc(), use rproc_free(). + */ +struct rproc *rproc_alloc(struct device *dev, const char *name, + const struct rproc_ops *ops, + const char *firmware, int len) +{ + struct rproc *rproc; + + if (!dev || !name || !ops) + return NULL; + + rproc = kzalloc(sizeof(struct rproc) + len, GFP_KERNEL); + if (!rproc) { + dev_err(dev, "%s: kzalloc failed\n", __func__); + return NULL; + } + + rproc->dev = dev; + rproc->name = name; + rproc->ops = ops; + rproc->firmware = firmware; + rproc->priv = &rproc[1]; + + atomic_set(&rproc->power, 0); + + kref_init(&rproc->refcount); + + mutex_init(&rproc->lock); + + INIT_LIST_HEAD(&rproc->carveouts); + INIT_LIST_HEAD(&rproc->mappings); + INIT_LIST_HEAD(&rproc->traces); + + rproc->state = RPROC_OFFLINE; + + return rproc; +} +EXPORT_SYMBOL(rproc_alloc); + +/** + * rproc_free() - free an rproc handle that was allocated by rproc_alloc + * @rproc: the remote processor handle + * + * This function should _only_ be used if @rproc was only allocated, + * but not registered yet. + * + * If @rproc was already successfully registered (by calling rproc_register()), + * then use rproc_unregister() instead. + */ +void rproc_free(struct rproc *rproc) +{ + kfree(rproc); +} +EXPORT_SYMBOL(rproc_free); + +/** + * rproc_unregister() - unregister a remote processor + * @rproc: rproc handle to unregister + * + * Unregisters a remote processor, and decrements its refcount. + * If its refcount drops to zero, then @rproc will be freed. If not, + * it will be freed later once the last reference is dropped. + * + * This function should be called when the platform specific rproc + * implementation decides to remove the rproc device. it should + * _only_ be called if a previous invocation of rproc_register() + * has completed successfully. + * + * After rproc_unregister() returns, @rproc is _not_ valid anymore and + * it shouldn't be used. More specifically, don't call rproc_free() + * or try to directly free @rproc after rproc_unregister() returns; + * none of these are needed, and calling them is a bug. + * + * Returns 0 on success and -EINVAL if @rproc isn't valid. + */ +int rproc_unregister(struct rproc *rproc) +{ + if (!rproc) + return -EINVAL; + + /* if rproc is just being registered, wait */ + wait_for_completion(&rproc->firmware_loading_complete); + + /* was an rpmsg vdev created ? */ + if (rproc->rvdev) + rproc_remove_rpmsg_vdev(rproc); + + klist_remove(&rproc->node); + + kref_put(&rproc->refcount, rproc_release); + + return 0; +} +EXPORT_SYMBOL(rproc_unregister); + +static int __init remoteproc_init(void) +{ + rproc_init_debugfs(); + return 0; +} +module_init(remoteproc_init); + +static void __exit remoteproc_exit(void) +{ + rproc_exit_debugfs(); +} +module_exit(remoteproc_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Generic Remote Processor Framework"); diff --git a/drivers/remoteproc/remoteproc_internal.h b/drivers/remoteproc/remoteproc_internal.h new file mode 100644 index 000000000000..8b2fc40e92d0 --- /dev/null +++ b/drivers/remoteproc/remoteproc_internal.h @@ -0,0 +1,44 @@ +/* + * Remote processor framework + * + * Copyright (C) 2011 Texas Instruments, Inc. + * Copyright (C) 2011 Google, Inc. + * + * Ohad Ben-Cohen + * Brian Swetland + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + */ + +#ifndef REMOTEPROC_INTERNAL_H +#define REMOTEPROC_INTERNAL_H + +#include + +struct rproc; + +/* from remoteproc_core.c */ +void rproc_release(struct kref *kref); +irqreturn_t rproc_vq_interrupt(struct rproc *rproc, int vq_id); + +/* from remoteproc_rpmsg.c */ +int rproc_add_rpmsg_vdev(struct rproc *); +void rproc_remove_rpmsg_vdev(struct rproc *rproc); + +/* from remoteproc_debugfs.c */ +void rproc_remove_trace_file(struct dentry *tfile); +struct dentry *rproc_create_trace_file(const char *name, struct rproc *rproc, + struct rproc_mem_entry *trace); +void rproc_delete_debug_dir(struct rproc *rproc); +void rproc_create_debug_dir(struct rproc *rproc); +void rproc_init_debugfs(void); +void rproc_exit_debugfs(void); + +#endif /* REMOTEPROC_INTERNAL_H */ diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h new file mode 100644 index 000000000000..1edbfde4593c --- /dev/null +++ b/include/linux/remoteproc.h @@ -0,0 +1,265 @@ +/* + * Remote Processor Framework + * + * Copyright(c) 2011 Texas Instruments, Inc. + * Copyright(c) 2011 Google, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Texas Instruments nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef REMOTEPROC_H +#define REMOTEPROC_H + +#include +#include +#include +#include +#include +#include + +/* + * The alignment between the consumer and producer parts of the vring. + * Note: this is part of the "wire" protocol. If you change this, you need + * to update your peers too. + */ +#define AMP_VRING_ALIGN (4096) + +/** + * struct fw_resource - describes an entry from the resource section + * @type: resource type + * @id: index number of the resource + * @da: device address of the resource + * @pa: physical address of the resource + * @len: size, in bytes, of the resource + * @flags: properties of the resource, e.g. iommu protection required + * @reserved: must be 0 atm + * @name: name of resource + * + * The remote processor firmware should contain a "resource table": + * array of 'struct fw_resource' entries. + * + * Some resources entries are mere announcements, where the host is informed + * of specific remoteproc configuration. Other entries require the host to + * do something (e.g. reserve a requested resource) and possibly also reply + * by overwriting a member inside 'struct fw_resource' with info about the + * allocated resource. + * + * Different resource entries use different members of this struct, + * with different meanings. This is pretty limiting and error-prone, + * so the plan is to move to variable-length TLV-based resource entries, + * where each resource type will have its own structure. + */ +struct fw_resource { + u32 type; + u32 id; + u64 da; + u64 pa; + u32 len; + u32 flags; + u8 reserved[16]; + u8 name[48]; +} __packed; + +/** + * enum fw_resource_type - types of resource entries + * + * @RSC_CARVEOUT: request for allocation of a physically contiguous + * memory region. + * @RSC_DEVMEM: request to iommu_map a memory-based peripheral. + * @RSC_TRACE: announces the availability of a trace buffer into which + * the remote processor will be writing logs. In this case, + * 'da' indicates the device address where logs are written to, + * and 'len' is the size of the trace buffer. + * @RSC_VRING: request for allocation of a virtio vring (address should + * be indicated in 'da', and 'len' should contain the number + * of buffers supported by the vring). + * @RSC_VIRTIO_DEV: this entry declares about support for a virtio device, + * and serves as the virtio header. 'da' holds the + * the virtio device features, 'pa' holds the virtio guest + * features, 'len' holds the virtio status, and 'flags' holds + * the virtio id (currently only VIRTIO_ID_RPMSG is supported). + * + * Most of the resource entries share the basic idea of address/length + * negotiation with the host: the firmware usually asks (on behalf of the + * remote processor that will soon be booted with it) for memory + * of size 'len' bytes, and the host needs to allocate it and provide + * the device/physical address (when relevant) in 'da'/'pa' respectively. + * + * If the firmware is compiled with hard coded device addresses, and + * can't handle dynamically allocated 'da' values, then the 'da' field + * will contain the expected device addresses (today we actually only support + * this scheme, as there aren't yet any use cases for dynamically allocated + * device addresses). + */ +enum fw_resource_type { + RSC_CARVEOUT = 0, + RSC_DEVMEM = 1, + RSC_TRACE = 2, + RSC_VRING = 3, + RSC_VIRTIO_DEV = 4, + RSC_VIRTIO_CFG = 5, +}; + +/** + * struct rproc_mem_entry - memory entry descriptor + * @va: virtual address + * @dma: dma address + * @len: length, in bytes + * @da: device address + * @priv: associated data + * @node: list node + */ +struct rproc_mem_entry { + void *va; + dma_addr_t dma; + int len; + u64 da; + void *priv; + struct list_head node; +}; + +struct rproc; + +/** + * struct rproc_ops - platform-specific device handlers + * @start: power on the device and boot it + * @stop: power off the device + * @kick: kick a virtqueue (virtqueue id given as a parameter) + */ +struct rproc_ops { + int (*start)(struct rproc *rproc); + int (*stop)(struct rproc *rproc); + void (*kick)(struct rproc *rproc, int vqid); +}; + +/** + * enum rproc_state - remote processor states + * @RPROC_OFFLINE: device is powered off + * @RPROC_SUSPENDED: device is suspended; needs to be woken up to receive + * a message. + * @RPROC_RUNNING: device is up and running + * @RPROC_CRASHED: device has crashed; need to start recovery + * @RPROC_LAST: just keep this one at the end + * + * Please note that the values of these states are used as indices + * to rproc_state_string, a state-to-name lookup table, + * so please keep the two synchronized. @RPROC_LAST is used to check + * the validity of an index before the lookup table is accessed, so + * please update it as needed too. + */ +enum rproc_state { + RPROC_OFFLINE = 0, + RPROC_SUSPENDED = 1, + RPROC_RUNNING = 2, + RPROC_CRASHED = 3, + RPROC_LAST = 4, +}; + +/** + * struct rproc - represents a physical remote processor device + * @node: klist node of this rproc object + * @domain: iommu domain + * @name: human readable name of the rproc + * @firmware: name of firmware file to be loaded + * @priv: private data which belongs to the platform-specific rproc module + * @ops: platform-specific start/stop rproc handlers + * @dev: underlying device + * @refcount: refcount of users that have a valid pointer to this rproc + * @power: refcount of users who need this rproc powered up + * @state: state of the device + * @lock: lock which protects concurrent manipulations of the rproc + * @dbg_dir: debugfs directory of this rproc device + * @traces: list of trace buffers + * @num_traces: number of trace buffers + * @carveouts: list of physically contiguous memory allocations + * @mappings: list of iommu mappings we initiated, needed on shutdown + * @firmware_loading_complete: marks e/o asynchronous firmware loading + * @bootaddr: address of first instruction to boot rproc with (optional) + * @rvdev: virtio device (we only support a single rpmsg virtio device for now) + */ +struct rproc { + struct klist_node node; + struct iommu_domain *domain; + const char *name; + const char *firmware; + void *priv; + const struct rproc_ops *ops; + struct device *dev; + struct kref refcount; + atomic_t power; + unsigned int state; + struct mutex lock; + struct dentry *dbg_dir; + struct list_head traces; + int num_traces; + struct list_head carveouts; + struct list_head mappings; + struct completion firmware_loading_complete; + u64 bootaddr; + struct rproc_vdev *rvdev; +}; + +/** + * struct rproc_vdev - remoteproc state for a supported virtio device + * @rproc: the rproc handle + * @vdev: the virio device + * @vq: the virtqueues for this vdev + * @vring: the vrings for this vdev + * @dfeatures: virtio device features + * @gfeatures: virtio guest features + */ +struct rproc_vdev { + struct rproc *rproc; + struct virtio_device vdev; + struct virtqueue *vq[2]; + struct rproc_mem_entry vring[2]; + unsigned long dfeatures; + unsigned long gfeatures; +}; + +struct rproc *rproc_get_by_name(const char *name); +void rproc_put(struct rproc *rproc); + +struct rproc *rproc_alloc(struct device *dev, const char *name, + const struct rproc_ops *ops, + const char *firmware, int len); +void rproc_free(struct rproc *rproc); +int rproc_register(struct rproc *rproc); +int rproc_unregister(struct rproc *rproc); + +int rproc_boot(struct rproc *rproc); +void rproc_shutdown(struct rproc *rproc); + +static inline struct rproc *vdev_to_rproc(struct virtio_device *vdev) +{ + struct rproc_vdev *rvdev = container_of(vdev, struct rproc_vdev, vdev); + + return rvdev->rproc; +} + +#endif /* REMOTEPROC_H */ -- cgit v1.2.3 From bcabbccabffe7326f046f25737ba1084f463c65c Mon Sep 17 00:00:00 2001 From: Ohad Ben-Cohen Date: Thu, 20 Oct 2011 21:10:55 +0200 Subject: rpmsg: add virtio-based remote processor messaging bus Add a virtio-based inter-processor communication bus, which enables kernel drivers to communicate with entities, running on remote processors, over shared memory using a simple messaging protocol. Every pair of AMP processors share two vrings, which are used to send and receive the messages over shared memory. The header of every message sent on the rpmsg bus contains src and dst addresses, which make it possible to multiplex several rpmsg channels on the same vring. Every rpmsg channel is a device on this bus. When a channel is added, and an appropriate rpmsg driver is found and probed, it is also assigned a local rpmsg address, which is then bound to the driver's callback. When inbound messages carry the local address of a bound driver, its callback is invoked by the bus. This patch provides a kernel interface only; user space interfaces will be later exposed by kernel users of this rpmsg bus. Designed with Brian Swetland . Signed-off-by: Ohad Ben-Cohen Acked-by: Rusty Russell (virtio_ids.h) Cc: Brian Swetland Cc: Arnd Bergmann Cc: Grant Likely Cc: Tony Lindgren Cc: Russell King Cc: Andrew Morton Cc: Greg KH Cc: Stephen Boyd --- Documentation/ABI/testing/sysfs-bus-rpmsg | 75 +++ Documentation/rpmsg.txt | 293 ++++++++ drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/rpmsg/Kconfig | 5 + drivers/rpmsg/Makefile | 1 + drivers/rpmsg/virtio_rpmsg_bus.c | 1026 +++++++++++++++++++++++++++++ include/linux/mod_devicetable.h | 9 + include/linux/rpmsg.h | 326 +++++++++ include/linux/virtio_ids.h | 1 + 10 files changed, 1739 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-bus-rpmsg create mode 100644 Documentation/rpmsg.txt create mode 100644 drivers/rpmsg/Kconfig create mode 100644 drivers/rpmsg/Makefile create mode 100644 drivers/rpmsg/virtio_rpmsg_bus.c create mode 100644 include/linux/rpmsg.h (limited to 'include/linux') diff --git a/Documentation/ABI/testing/sysfs-bus-rpmsg b/Documentation/ABI/testing/sysfs-bus-rpmsg new file mode 100644 index 000000000000..189e419a5a2d --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-rpmsg @@ -0,0 +1,75 @@ +What: /sys/bus/rpmsg/devices/.../name +Date: June 2011 +KernelVersion: 3.3 +Contact: Ohad Ben-Cohen +Description: + Every rpmsg device is a communication channel with a remote + processor. Channels are identified with a (textual) name, + which is maximum 32 bytes long (defined as RPMSG_NAME_SIZE in + rpmsg.h). + + This sysfs entry contains the name of this channel. + +What: /sys/bus/rpmsg/devices/.../src +Date: June 2011 +KernelVersion: 3.3 +Contact: Ohad Ben-Cohen +Description: + Every rpmsg device is a communication channel with a remote + processor. Channels have a local ("source") rpmsg address, + and remote ("destination") rpmsg address. When an entity + starts listening on one end of a channel, it assigns it with + a unique rpmsg address (a 32 bits integer). This way when + inbound messages arrive to this address, the rpmsg core + dispatches them to the listening entity (a kernel driver). + + This sysfs entry contains the src (local) rpmsg address + of this channel. If it contains 0xffffffff, then an address + wasn't assigned (can happen if no driver exists for this + channel). + +What: /sys/bus/rpmsg/devices/.../dst +Date: June 2011 +KernelVersion: 3.3 +Contact: Ohad Ben-Cohen +Description: + Every rpmsg device is a communication channel with a remote + processor. Channels have a local ("source") rpmsg address, + and remote ("destination") rpmsg address. When an entity + starts listening on one end of a channel, it assigns it with + a unique rpmsg address (a 32 bits integer). This way when + inbound messages arrive to this address, the rpmsg core + dispatches them to the listening entity. + + This sysfs entry contains the dst (remote) rpmsg address + of this channel. If it contains 0xffffffff, then an address + wasn't assigned (can happen if the kernel driver that + is attached to this channel is exposing a service to the + remote processor. This make it a local rpmsg server, + and it is listening for inbound messages that may be sent + from any remote rpmsg client; it is not bound to a single + remote entity). + +What: /sys/bus/rpmsg/devices/.../announce +Date: June 2011 +KernelVersion: 3.3 +Contact: Ohad Ben-Cohen +Description: + Every rpmsg device is a communication channel with a remote + processor. Channels are identified by a textual name (see + /sys/bus/rpmsg/devices/.../name above) and have a local + ("source") rpmsg address, and remote ("destination") rpmsg + address. + + A channel is first created when an entity, whether local + or remote, starts listening on it for messages (and is thus + called an rpmsg server). + + When that happens, a "name service" announcement is sent + to the other processor, in order to let it know about the + creation of the channel (this way remote clients know they + can start sending messages). + + This sysfs entry tells us whether the channel is a local + server channel that is announced (values are either + true or false). diff --git a/Documentation/rpmsg.txt b/Documentation/rpmsg.txt new file mode 100644 index 000000000000..409d9f964c5b --- /dev/null +++ b/Documentation/rpmsg.txt @@ -0,0 +1,293 @@ +Remote Processor Messaging (rpmsg) Framework + +Note: this document describes the rpmsg bus and how to write rpmsg drivers. +To learn how to add rpmsg support for new platforms, check out remoteproc.txt +(also a resident of Documentation/). + +1. Introduction + +Modern SoCs typically employ heterogeneous remote processor devices in +asymmetric multiprocessing (AMP) configurations, which may be running +different instances of operating system, whether it's Linux or any other +flavor of real-time OS. + +OMAP4, for example, has dual Cortex-A9, dual Cortex-M3 and a C64x+ DSP. +Typically, the dual cortex-A9 is running Linux in a SMP configuration, +and each of the other three cores (two M3 cores and a DSP) is running +its own instance of RTOS in an AMP configuration. + +Typically AMP remote processors employ dedicated DSP codecs and multimedia +hardware accelerators, and therefore are often used to offload CPU-intensive +multimedia tasks from the main application processor. + +These remote processors could also be used to control latency-sensitive +sensors, drive random hardware blocks, or just perform background tasks +while the main CPU is idling. + +Users of those remote processors can either be userland apps (e.g. multimedia +frameworks talking with remote OMX components) or kernel drivers (controlling +hardware accessible only by the remote processor, reserving kernel-controlled +resources on behalf of the remote processor, etc..). + +Rpmsg is a virtio-based messaging bus that allows kernel drivers to communicate +with remote processors available on the system. In turn, drivers could then +expose appropriate user space interfaces, if needed. + +When writing a driver that exposes rpmsg communication to userland, please +keep in mind that remote processors might have direct access to the +system's physical memory and other sensitive hardware resources (e.g. on +OMAP4, remote cores and hardware accelerators may have direct access to the +physical memory, gpio banks, dma controllers, i2c bus, gptimers, mailbox +devices, hwspinlocks, etc..). Moreover, those remote processors might be +running RTOS where every task can access the entire memory/devices exposed +to the processor. To minimize the risks of rogue (or buggy) userland code +exploiting remote bugs, and by that taking over the system, it is often +desired to limit userland to specific rpmsg channels (see definition below) +it can send messages on, and if possible, minimize how much control +it has over the content of the messages. + +Every rpmsg device is a communication channel with a remote processor (thus +rpmsg devices are called channels). Channels are identified by a textual name +and have a local ("source") rpmsg address, and remote ("destination") rpmsg +address. + +When a driver starts listening on a channel, its rx callback is bound with +a unique rpmsg local address (a 32-bit integer). This way when inbound messages +arrive, the rpmsg core dispatches them to the appropriate driver according +to their destination address (this is done by invoking the driver's rx handler +with the payload of the inbound message). + + +2. User API + + int rpmsg_send(struct rpmsg_channel *rpdev, void *data, int len); + - sends a message across to the remote processor on a given channel. + The caller should specify the channel, the data it wants to send, + and its length (in bytes). The message will be sent on the specified + channel, i.e. its source and destination address fields will be + set to the channel's src and dst addresses. + + In case there are no TX buffers available, the function will block until + one becomes available (i.e. until the remote processor consumes + a tx buffer and puts it back on virtio's used descriptor ring), + or a timeout of 15 seconds elapses. When the latter happens, + -ERESTARTSYS is returned. + The function can only be called from a process context (for now). + Returns 0 on success and an appropriate error value on failure. + + int rpmsg_sendto(struct rpmsg_channel *rpdev, void *data, int len, u32 dst); + - sends a message across to the remote processor on a given channel, + to a destination address provided by the caller. + The caller should specify the channel, the data it wants to send, + its length (in bytes), and an explicit destination address. + The message will then be sent to the remote processor to which the + channel belongs, using the channel's src address, and the user-provided + dst address (thus the channel's dst address will be ignored). + + In case there are no TX buffers available, the function will block until + one becomes available (i.e. until the remote processor consumes + a tx buffer and puts it back on virtio's used descriptor ring), + or a timeout of 15 seconds elapses. When the latter happens, + -ERESTARTSYS is returned. + The function can only be called from a process context (for now). + Returns 0 on success and an appropriate error value on failure. + + int rpmsg_send_offchannel(struct rpmsg_channel *rpdev, u32 src, u32 dst, + void *data, int len); + - sends a message across to the remote processor, using the src and dst + addresses provided by the user. + The caller should specify the channel, the data it wants to send, + its length (in bytes), and explicit source and destination addresses. + The message will then be sent to the remote processor to which the + channel belongs, but the channel's src and dst addresses will be + ignored (and the user-provided addresses will be used instead). + + In case there are no TX buffers available, the function will block until + one becomes available (i.e. until the remote processor consumes + a tx buffer and puts it back on virtio's used descriptor ring), + or a timeout of 15 seconds elapses. When the latter happens, + -ERESTARTSYS is returned. + The function can only be called from a process context (for now). + Returns 0 on success and an appropriate error value on failure. + + int rpmsg_trysend(struct rpmsg_channel *rpdev, void *data, int len); + - sends a message across to the remote processor on a given channel. + The caller should specify the channel, the data it wants to send, + and its length (in bytes). The message will be sent on the specified + channel, i.e. its source and destination address fields will be + set to the channel's src and dst addresses. + + In case there are no TX buffers available, the function will immediately + return -ENOMEM without waiting until one becomes available. + The function can only be called from a process context (for now). + Returns 0 on success and an appropriate error value on failure. + + int rpmsg_trysendto(struct rpmsg_channel *rpdev, void *data, int len, u32 dst) + - sends a message across to the remote processor on a given channel, + to a destination address provided by the user. + The user should specify the channel, the data it wants to send, + its length (in bytes), and an explicit destination address. + The message will then be sent to the remote processor to which the + channel belongs, using the channel's src address, and the user-provided + dst address (thus the channel's dst address will be ignored). + + In case there are no TX buffers available, the function will immediately + return -ENOMEM without waiting until one becomes available. + The function can only be called from a process context (for now). + Returns 0 on success and an appropriate error value on failure. + + int rpmsg_trysend_offchannel(struct rpmsg_channel *rpdev, u32 src, u32 dst, + void *data, int len); + - sends a message across to the remote processor, using source and + destination addresses provided by the user. + The user should specify the channel, the data it wants to send, + its length (in bytes), and explicit source and destination addresses. + The message will then be sent to the remote processor to which the + channel belongs, but the channel's src and dst addresses will be + ignored (and the user-provided addresses will be used instead). + + In case there are no TX buffers available, the function will immediately + return -ENOMEM without waiting until one becomes available. + The function can only be called from a process context (for now). + Returns 0 on success and an appropriate error value on failure. + + struct rpmsg_endpoint *rpmsg_create_ept(struct rpmsg_channel *rpdev, + void (*cb)(struct rpmsg_channel *, void *, int, void *, u32), + void *priv, u32 addr); + - every rpmsg address in the system is bound to an rx callback (so when + inbound messages arrive, they are dispatched by the rpmsg bus using the + appropriate callback handler) by means of an rpmsg_endpoint struct. + + This function allows drivers to create such an endpoint, and by that, + bind a callback, and possibly some private data too, to an rpmsg address + (either one that is known in advance, or one that will be dynamically + assigned for them). + + Simple rpmsg drivers need not call rpmsg_create_ept, because an endpoint + is already created for them when they are probed by the rpmsg bus + (using the rx callback they provide when they registered to the rpmsg bus). + + So things should just work for simple drivers: they already have an + endpoint, their rx callback is bound to their rpmsg address, and when + relevant inbound messages arrive (i.e. messages which their dst address + equals to the src address of their rpmsg channel), the driver's handler + is invoked to process it. + + That said, more complicated drivers might do need to allocate + additional rpmsg addresses, and bind them to different rx callbacks. + To accomplish that, those drivers need to call this function. + Drivers should provide their channel (so the new endpoint would bind + to the same remote processor their channel belongs to), an rx callback + function, an optional private data (which is provided back when the + rx callback is invoked), and an address they want to bind with the + callback. If addr is RPMSG_ADDR_ANY, then rpmsg_create_ept will + dynamically assign them an available rpmsg address (drivers should have + a very good reason why not to always use RPMSG_ADDR_ANY here). + + Returns a pointer to the endpoint on success, or NULL on error. + + void rpmsg_destroy_ept(struct rpmsg_endpoint *ept); + - destroys an existing rpmsg endpoint. user should provide a pointer + to an rpmsg endpoint that was previously created with rpmsg_create_ept(). + + int register_rpmsg_driver(struct rpmsg_driver *rpdrv); + - registers an rpmsg driver with the rpmsg bus. user should provide + a pointer to an rpmsg_driver struct, which contains the driver's + ->probe() and ->remove() functions, an rx callback, and an id_table + specifying the names of the channels this driver is interested to + be probed with. + + void unregister_rpmsg_driver(struct rpmsg_driver *rpdrv); + - unregisters an rpmsg driver from the rpmsg bus. user should provide + a pointer to a previously-registered rpmsg_driver struct. + Returns 0 on success, and an appropriate error value on failure. + + +3. Typical usage + +The following is a simple rpmsg driver, that sends an "hello!" message +on probe(), and whenever it receives an incoming message, it dumps its +content to the console. + +#include +#include +#include + +static void rpmsg_sample_cb(struct rpmsg_channel *rpdev, void *data, int len, + void *priv, u32 src) +{ + print_hex_dump(KERN_INFO, "incoming message:", DUMP_PREFIX_NONE, + 16, 1, data, len, true); +} + +static int rpmsg_sample_probe(struct rpmsg_channel *rpdev) +{ + int err; + + dev_info(&rpdev->dev, "chnl: 0x%x -> 0x%x\n", rpdev->src, rpdev->dst); + + /* send a message on our channel */ + err = rpmsg_send(rpdev, "hello!", 6); + if (err) { + pr_err("rpmsg_send failed: %d\n", err); + return err; + } + + return 0; +} + +static void __devexit rpmsg_sample_remove(struct rpmsg_channel *rpdev) +{ + dev_info(&rpdev->dev, "rpmsg sample client driver is removed\n"); +} + +static struct rpmsg_device_id rpmsg_driver_sample_id_table[] = { + { .name = "rpmsg-client-sample" }, + { }, +}; +MODULE_DEVICE_TABLE(rpmsg, rpmsg_driver_sample_id_table); + +static struct rpmsg_driver rpmsg_sample_client = { + .drv.name = KBUILD_MODNAME, + .drv.owner = THIS_MODULE, + .id_table = rpmsg_driver_sample_id_table, + .probe = rpmsg_sample_probe, + .callback = rpmsg_sample_cb, + .remove = __devexit_p(rpmsg_sample_remove), +}; + +static int __init init(void) +{ + return register_rpmsg_driver(&rpmsg_sample_client); +} +module_init(init); + +static void __exit fini(void) +{ + unregister_rpmsg_driver(&rpmsg_sample_client); +} +module_exit(fini); + +Note: a similar sample which can be built and loaded can be found +in samples/rpmsg/. + +4. Allocations of rpmsg channels: + +At this point we only support dynamic allocations of rpmsg channels. + +This is possible only with remote processors that have the VIRTIO_RPMSG_F_NS +virtio device feature set. This feature bit means that the remote +processor supports dynamic name service announcement messages. + +When this feature is enabled, creation of rpmsg devices (i.e. channels) +is completely dynamic: the remote processor announces the existence of a +remote rpmsg service by sending a name service message (which contains +the name and rpmsg addr of the remote service, see struct rpmsg_ns_msg). + +This message is then handled by the rpmsg bus, which in turn dynamically +creates and registers an rpmsg channel (which represents the remote service). +If/when a relevant rpmsg driver is registered, it will be immediately probed +by the bus, and can then start sending messages to the remote service. + +The plan is also to add static creation of rpmsg channels via the virtio +config space, but it's not implemented yet. diff --git a/drivers/Kconfig b/drivers/Kconfig index 27b34bf41d4c..516faf6d88ba 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -134,6 +134,8 @@ source "drivers/iommu/Kconfig" source "drivers/remoteproc/Kconfig" +source "drivers/rpmsg/Kconfig" + source "drivers/virt/Kconfig" source "drivers/devfreq/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index f1019b714f27..3fdc17709d36 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -127,6 +127,7 @@ obj-$(CONFIG_HWSPINLOCK) += hwspinlock/ obj-$(CONFIG_NFC) += nfc/ obj-$(CONFIG_IOMMU_SUPPORT) += iommu/ obj-$(CONFIG_REMOTEPROC) += remoteproc/ +obj-$(CONFIG_RPMSG) += rpmsg/ # Virtualization drivers obj-$(CONFIG_VIRT_DRIVERS) += virt/ diff --git a/drivers/rpmsg/Kconfig b/drivers/rpmsg/Kconfig new file mode 100644 index 000000000000..811fede35bd8 --- /dev/null +++ b/drivers/rpmsg/Kconfig @@ -0,0 +1,5 @@ +# RPMSG always gets selected by whoever wants it +config RPMSG + tristate + select VIRTIO + select VIRTIO_RING diff --git a/drivers/rpmsg/Makefile b/drivers/rpmsg/Makefile new file mode 100644 index 000000000000..7617fcb8259f --- /dev/null +++ b/drivers/rpmsg/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_RPMSG) += virtio_rpmsg_bus.o diff --git a/drivers/rpmsg/virtio_rpmsg_bus.c b/drivers/rpmsg/virtio_rpmsg_bus.c new file mode 100644 index 000000000000..257683e7fe8a --- /dev/null +++ b/drivers/rpmsg/virtio_rpmsg_bus.c @@ -0,0 +1,1026 @@ +/* + * Virtio-based remote processor messaging bus + * + * Copyright (C) 2011 Texas Instruments, Inc. + * Copyright (C) 2011 Google, Inc. + * + * Ohad Ben-Cohen + * Brian Swetland + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * struct virtproc_info - virtual remote processor state + * @vdev: the virtio device + * @rvq: rx virtqueue + * @svq: tx virtqueue + * @rbufs: kernel address of rx buffers + * @sbufs: kernel address of tx buffers + * @last_sbuf: index of last tx buffer used + * @bufs_dma: dma base addr of the buffers + * @tx_lock: protects svq, sbufs and sleepers, to allow concurrent senders. + * sending a message might require waking up a dozing remote + * processor, which involves sleeping, hence the mutex. + * @endpoints: idr of local endpoints, allows fast retrieval + * @endpoints_lock: lock of the endpoints set + * @sendq: wait queue of sending contexts waiting for a tx buffers + * @sleepers: number of senders that are waiting for a tx buffer + * @ns_ept: the bus's name service endpoint + * + * This structure stores the rpmsg state of a given virtio remote processor + * device (there might be several virtio proc devices for each physical + * remote processor). + */ +struct virtproc_info { + struct virtio_device *vdev; + struct virtqueue *rvq, *svq; + void *rbufs, *sbufs; + int last_sbuf; + dma_addr_t bufs_dma; + struct mutex tx_lock; + struct idr endpoints; + struct mutex endpoints_lock; + wait_queue_head_t sendq; + atomic_t sleepers; + struct rpmsg_endpoint *ns_ept; +}; + +/** + * struct rpmsg_channel_info - internal channel info representation + * @name: name of service + * @src: local address + * @dst: destination address + */ +struct rpmsg_channel_info { + char name[RPMSG_NAME_SIZE]; + u32 src; + u32 dst; +}; + +#define to_rpmsg_channel(d) container_of(d, struct rpmsg_channel, dev) +#define to_rpmsg_driver(d) container_of(d, struct rpmsg_driver, drv) + +/* + * We're allocating 512 buffers of 512 bytes for communications, and then + * using the first 256 buffers for RX, and the last 256 buffers for TX. + * + * Each buffer will have 16 bytes for the msg header and 496 bytes for + * the payload. + * + * This will require a total space of 256KB for the buffers. + * + * We might also want to add support for user-provided buffers in time. + * This will allow bigger buffer size flexibility, and can also be used + * to achieve zero-copy messaging. + * + * Note that these numbers are purely a decision of this driver - we + * can change this without changing anything in the firmware of the remote + * processor. + */ +#define RPMSG_NUM_BUFS (512) +#define RPMSG_BUF_SIZE (512) +#define RPMSG_TOTAL_BUF_SPACE (RPMSG_NUM_BUFS * RPMSG_BUF_SIZE) + +/* + * Local addresses are dynamically allocated on-demand. + * We do not dynamically assign addresses from the low 1024 range, + * in order to reserve that address range for predefined services. + */ +#define RPMSG_RESERVED_ADDRESSES (1024) + +/* Address 53 is reserved for advertising remote services */ +#define RPMSG_NS_ADDR (53) + +/* sysfs show configuration fields */ +#define rpmsg_show_attr(field, path, format_string) \ +static ssize_t \ +field##_show(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + struct rpmsg_channel *rpdev = to_rpmsg_channel(dev); \ + \ + return sprintf(buf, format_string, rpdev->path); \ +} + +/* for more info, see Documentation/ABI/testing/sysfs-bus-rpmsg */ +rpmsg_show_attr(name, id.name, "%s\n"); +rpmsg_show_attr(src, src, "0x%x\n"); +rpmsg_show_attr(dst, dst, "0x%x\n"); +rpmsg_show_attr(announce, announce ? "true" : "false", "%s\n"); + +/* + * Unique (and free running) index for rpmsg devices. + * + * Yeah, we're not recycling those numbers (yet?). will be easy + * to change if/when we want to. + */ +static unsigned int rpmsg_dev_index; + +static ssize_t modalias_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct rpmsg_channel *rpdev = to_rpmsg_channel(dev); + + return sprintf(buf, RPMSG_DEVICE_MODALIAS_FMT "\n", rpdev->id.name); +} + +static struct device_attribute rpmsg_dev_attrs[] = { + __ATTR_RO(name), + __ATTR_RO(modalias), + __ATTR_RO(dst), + __ATTR_RO(src), + __ATTR_RO(announce), + __ATTR_NULL +}; + +/* rpmsg devices and drivers are matched using the service name */ +static inline int rpmsg_id_match(const struct rpmsg_channel *rpdev, + const struct rpmsg_device_id *id) +{ + return strncmp(id->name, rpdev->id.name, RPMSG_NAME_SIZE) == 0; +} + +/* match rpmsg channel and rpmsg driver */ +static int rpmsg_dev_match(struct device *dev, struct device_driver *drv) +{ + struct rpmsg_channel *rpdev = to_rpmsg_channel(dev); + struct rpmsg_driver *rpdrv = to_rpmsg_driver(drv); + const struct rpmsg_device_id *ids = rpdrv->id_table; + unsigned int i; + + for (i = 0; ids[i].name[0]; i++) + if (rpmsg_id_match(rpdev, &ids[i])) + return 1; + + return 0; +} + +static int rpmsg_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + struct rpmsg_channel *rpdev = to_rpmsg_channel(dev); + + return add_uevent_var(env, "MODALIAS=" RPMSG_DEVICE_MODALIAS_FMT, + rpdev->id.name); +} + +/* for more info, see below documentation of rpmsg_create_ept() */ +static struct rpmsg_endpoint *__rpmsg_create_ept(struct virtproc_info *vrp, + struct rpmsg_channel *rpdev, rpmsg_rx_cb_t cb, + void *priv, u32 addr) +{ + int err, tmpaddr, request; + struct rpmsg_endpoint *ept; + struct device *dev = rpdev ? &rpdev->dev : &vrp->vdev->dev; + + if (!idr_pre_get(&vrp->endpoints, GFP_KERNEL)) + return NULL; + + ept = kzalloc(sizeof(*ept), GFP_KERNEL); + if (!ept) { + dev_err(dev, "failed to kzalloc a new ept\n"); + return NULL; + } + + ept->rpdev = rpdev; + ept->cb = cb; + ept->priv = priv; + + /* do we need to allocate a local address ? */ + request = addr == RPMSG_ADDR_ANY ? RPMSG_RESERVED_ADDRESSES : addr; + + mutex_lock(&vrp->endpoints_lock); + + /* bind the endpoint to an rpmsg address (and allocate one if needed) */ + err = idr_get_new_above(&vrp->endpoints, ept, request, &tmpaddr); + if (err) { + dev_err(dev, "idr_get_new_above failed: %d\n", err); + goto free_ept; + } + + /* make sure the user's address request is fulfilled, if relevant */ + if (addr != RPMSG_ADDR_ANY && tmpaddr != addr) { + dev_err(dev, "address 0x%x already in use\n", addr); + goto rem_idr; + } + + ept->addr = tmpaddr; + + mutex_unlock(&vrp->endpoints_lock); + + return ept; + +rem_idr: + idr_remove(&vrp->endpoints, request); +free_ept: + mutex_unlock(&vrp->endpoints_lock); + kfree(ept); + return NULL; +} + +/** + * rpmsg_create_ept() - create a new rpmsg_endpoint + * @rpdev: rpmsg channel device + * @cb: rx callback handler + * @priv: private data for the driver's use + * @addr: local rpmsg address to bind with @cb + * + * Every rpmsg address in the system is bound to an rx callback (so when + * inbound messages arrive, they are dispatched by the rpmsg bus using the + * appropriate callback handler) by means of an rpmsg_endpoint struct. + * + * This function allows drivers to create such an endpoint, and by that, + * bind a callback, and possibly some private data too, to an rpmsg address + * (either one that is known in advance, or one that will be dynamically + * assigned for them). + * + * Simple rpmsg drivers need not call rpmsg_create_ept, because an endpoint + * is already created for them when they are probed by the rpmsg bus + * (using the rx callback provided when they registered to the rpmsg bus). + * + * So things should just work for simple drivers: they already have an + * endpoint, their rx callback is bound to their rpmsg address, and when + * relevant inbound messages arrive (i.e. messages which their dst address + * equals to the src address of their rpmsg channel), the driver's handler + * is invoked to process it. + * + * That said, more complicated drivers might do need to allocate + * additional rpmsg addresses, and bind them to different rx callbacks. + * To accomplish that, those drivers need to call this function. + * + * Drivers should provide their @rpdev channel (so the new endpoint would belong + * to the same remote processor their channel belongs to), an rx callback + * function, an optional private data (which is provided back when the + * rx callback is invoked), and an address they want to bind with the + * callback. If @addr is RPMSG_ADDR_ANY, then rpmsg_create_ept will + * dynamically assign them an available rpmsg address (drivers should have + * a very good reason why not to always use RPMSG_ADDR_ANY here). + * + * Returns a pointer to the endpoint on success, or NULL on error. + */ +struct rpmsg_endpoint *rpmsg_create_ept(struct rpmsg_channel *rpdev, + rpmsg_rx_cb_t cb, void *priv, u32 addr) +{ + return __rpmsg_create_ept(rpdev->vrp, rpdev, cb, priv, addr); +} +EXPORT_SYMBOL(rpmsg_create_ept); + +/** + * rpmsg_destroy_ept() - destroy an existing rpmsg endpoint + * @ept: endpoing to destroy + * + * Should be used by drivers to destroy an rpmsg endpoint previously + * created with rpmsg_create_ept(). + */ +void rpmsg_destroy_ept(struct rpmsg_endpoint *ept) +{ + struct virtproc_info *vrp = ept->rpdev->vrp; + + mutex_lock(&vrp->endpoints_lock); + idr_remove(&vrp->endpoints, ept->addr); + mutex_unlock(&vrp->endpoints_lock); + + kfree(ept); +} +EXPORT_SYMBOL(rpmsg_destroy_ept); + +/* + * when an rpmsg driver is probed with a channel, we seamlessly create + * it an endpoint, binding its rx callback to a unique local rpmsg + * address. + * + * if we need to, we also announce about this channel to the remote + * processor (needed in case the driver is exposing an rpmsg service). + */ +static int rpmsg_dev_probe(struct device *dev) +{ + struct rpmsg_channel *rpdev = to_rpmsg_channel(dev); + struct rpmsg_driver *rpdrv = to_rpmsg_driver(rpdev->dev.driver); + struct virtproc_info *vrp = rpdev->vrp; + struct rpmsg_endpoint *ept; + int err; + + ept = rpmsg_create_ept(rpdev, rpdrv->callback, NULL, rpdev->src); + if (!ept) { + dev_err(dev, "failed to create endpoint\n"); + err = -ENOMEM; + goto out; + } + + rpdev->ept = ept; + rpdev->src = ept->addr; + + err = rpdrv->probe(rpdev); + if (err) { + dev_err(dev, "%s: failed: %d\n", __func__, err); + rpmsg_destroy_ept(ept); + goto out; + } + + /* need to tell remote processor's name service about this channel ? */ + if (rpdev->announce && + virtio_has_feature(vrp->vdev, VIRTIO_RPMSG_F_NS)) { + struct rpmsg_ns_msg nsm; + + strncpy(nsm.name, rpdev->id.name, RPMSG_NAME_SIZE); + nsm.addr = rpdev->src; + nsm.flags = RPMSG_NS_CREATE; + + err = rpmsg_sendto(rpdev, &nsm, sizeof(nsm), RPMSG_NS_ADDR); + if (err) + dev_err(dev, "failed to announce service %d\n", err); + } + +out: + return err; +} + +static int rpmsg_dev_remove(struct device *dev) +{ + struct rpmsg_channel *rpdev = to_rpmsg_channel(dev); + struct rpmsg_driver *rpdrv = to_rpmsg_driver(rpdev->dev.driver); + struct virtproc_info *vrp = rpdev->vrp; + int err = 0; + + /* tell remote processor's name service we're removing this channel */ + if (rpdev->announce && + virtio_has_feature(vrp->vdev, VIRTIO_RPMSG_F_NS)) { + struct rpmsg_ns_msg nsm; + + strncpy(nsm.name, rpdev->id.name, RPMSG_NAME_SIZE); + nsm.addr = rpdev->src; + nsm.flags = RPMSG_NS_DESTROY; + + err = rpmsg_sendto(rpdev, &nsm, sizeof(nsm), RPMSG_NS_ADDR); + if (err) + dev_err(dev, "failed to announce service %d\n", err); + } + + rpdrv->remove(rpdev); + + rpmsg_destroy_ept(rpdev->ept); + + return err; +} + +static struct bus_type rpmsg_bus = { + .name = "rpmsg", + .match = rpmsg_dev_match, + .dev_attrs = rpmsg_dev_attrs, + .uevent = rpmsg_uevent, + .probe = rpmsg_dev_probe, + .remove = rpmsg_dev_remove, +}; + +/** + * register_rpmsg_driver() - register an rpmsg driver with the rpmsg bus + * @rpdrv: pointer to a struct rpmsg_driver + * + * Returns 0 on success, and an appropriate error value on failure. + */ +int register_rpmsg_driver(struct rpmsg_driver *rpdrv) +{ + rpdrv->drv.bus = &rpmsg_bus; + return driver_register(&rpdrv->drv); +} +EXPORT_SYMBOL(register_rpmsg_driver); + +/** + * unregister_rpmsg_driver() - unregister an rpmsg driver from the rpmsg bus + * @rpdrv: pointer to a struct rpmsg_driver + * + * Returns 0 on success, and an appropriate error value on failure. + */ +void unregister_rpmsg_driver(struct rpmsg_driver *rpdrv) +{ + driver_unregister(&rpdrv->drv); +} +EXPORT_SYMBOL(unregister_rpmsg_driver); + +static void rpmsg_release_device(struct device *dev) +{ + struct rpmsg_channel *rpdev = to_rpmsg_channel(dev); + + kfree(rpdev); +} + +/* + * match an rpmsg channel with a channel info struct. + * this is used to make sure we're not creating rpmsg devices for channels + * that already exist. + */ +static int rpmsg_channel_match(struct device *dev, void *data) +{ + struct rpmsg_channel_info *chinfo = data; + struct rpmsg_channel *rpdev = to_rpmsg_channel(dev); + + if (chinfo->src != RPMSG_ADDR_ANY && chinfo->src != rpdev->src) + return 0; + + if (chinfo->dst != RPMSG_ADDR_ANY && chinfo->dst != rpdev->dst) + return 0; + + if (strncmp(chinfo->name, rpdev->id.name, RPMSG_NAME_SIZE)) + return 0; + + /* found a match ! */ + return 1; +} + +/* + * create an rpmsg channel using its name and address info. + * this function will be used to create both static and dynamic + * channels. + */ +static struct rpmsg_channel *rpmsg_create_channel(struct virtproc_info *vrp, + struct rpmsg_channel_info *chinfo) +{ + struct rpmsg_channel *rpdev; + struct device *tmp, *dev = &vrp->vdev->dev; + int ret; + + /* make sure a similar channel doesn't already exist */ + tmp = device_find_child(dev, chinfo, rpmsg_channel_match); + if (tmp) { + /* decrement the matched device's refcount back */ + put_device(tmp); + dev_err(dev, "channel %s:%x:%x already exist\n", + chinfo->name, chinfo->src, chinfo->dst); + return NULL; + } + + rpdev = kzalloc(sizeof(struct rpmsg_channel), GFP_KERNEL); + if (!rpdev) { + pr_err("kzalloc failed\n"); + return NULL; + } + + rpdev->vrp = vrp; + rpdev->src = chinfo->src; + rpdev->dst = chinfo->dst; + + /* + * rpmsg server channels has predefined local address (for now), + * and their existence needs to be announced remotely + */ + rpdev->announce = rpdev->src != RPMSG_ADDR_ANY ? true : false; + + strncpy(rpdev->id.name, chinfo->name, RPMSG_NAME_SIZE); + + /* very simple device indexing plumbing which is enough for now */ + dev_set_name(&rpdev->dev, "rpmsg%d", rpmsg_dev_index++); + + rpdev->dev.parent = &vrp->vdev->dev; + rpdev->dev.bus = &rpmsg_bus; + rpdev->dev.release = rpmsg_release_device; + + ret = device_register(&rpdev->dev); + if (ret) { + dev_err(dev, "device_register failed: %d\n", ret); + put_device(&rpdev->dev); + return NULL; + } + + return rpdev; +} + +/* + * find an existing channel using its name + address properties, + * and destroy it + */ +static int rpmsg_destroy_channel(struct virtproc_info *vrp, + struct rpmsg_channel_info *chinfo) +{ + struct virtio_device *vdev = vrp->vdev; + struct device *dev; + + dev = device_find_child(&vdev->dev, chinfo, rpmsg_channel_match); + if (!dev) + return -EINVAL; + + device_unregister(dev); + + put_device(dev); + + return 0; +} + +/* super simple buffer "allocator" that is just enough for now */ +static void *get_a_tx_buf(struct virtproc_info *vrp) +{ + unsigned int len; + void *ret; + + /* support multiple concurrent senders */ + mutex_lock(&vrp->tx_lock); + + /* + * either pick the next unused tx buffer + * (half of our buffers are used for sending messages) + */ + if (vrp->last_sbuf < RPMSG_NUM_BUFS / 2) + ret = vrp->sbufs + RPMSG_BUF_SIZE * vrp->last_sbuf++; + /* or recycle a used one */ + else + ret = virtqueue_get_buf(vrp->svq, &len); + + mutex_unlock(&vrp->tx_lock); + + return ret; +} + +/** + * rpmsg_upref_sleepers() - enable "tx-complete" interrupts, if needed + * @vrp: virtual remote processor state + * + * This function is called before a sender is blocked, waiting for + * a tx buffer to become available. + * + * If we already have blocking senders, this function merely increases + * the "sleepers" reference count, and exits. + * + * Otherwise, if this is the first sender to block, we also enable + * virtio's tx callbacks, so we'd be immediately notified when a tx + * buffer is consumed (we rely on virtio's tx callback in order + * to wake up sleeping senders as soon as a tx buffer is used by the + * remote processor). + */ +static void rpmsg_upref_sleepers(struct virtproc_info *vrp) +{ + /* support multiple concurrent senders */ + mutex_lock(&vrp->tx_lock); + + /* are we the first sleeping context waiting for tx buffers ? */ + if (atomic_inc_return(&vrp->sleepers) == 1) + /* enable "tx-complete" interrupts before dozing off */ + virtqueue_enable_cb(vrp->svq); + + mutex_unlock(&vrp->tx_lock); +} + +/** + * rpmsg_downref_sleepers() - disable "tx-complete" interrupts, if needed + * @vrp: virtual remote processor state + * + * This function is called after a sender, that waited for a tx buffer + * to become available, is unblocked. + * + * If we still have blocking senders, this function merely decreases + * the "sleepers" reference count, and exits. + * + * Otherwise, if there are no more blocking senders, we also disable + * virtio's tx callbacks, to avoid the overhead incurred with handling + * those (now redundant) interrupts. + */ +static void rpmsg_downref_sleepers(struct virtproc_info *vrp) +{ + /* support multiple concurrent senders */ + mutex_lock(&vrp->tx_lock); + + /* are we the last sleeping context waiting for tx buffers ? */ + if (atomic_dec_and_test(&vrp->sleepers)) + /* disable "tx-complete" interrupts */ + virtqueue_disable_cb(vrp->svq); + + mutex_unlock(&vrp->tx_lock); +} + +/** + * rpmsg_send_offchannel_raw() - send a message across to the remote processor + * @rpdev: the rpmsg channel + * @src: source address + * @dst: destination address + * @data: payload of message + * @len: length of payload + * @wait: indicates whether caller should block in case no TX buffers available + * + * This function is the base implementation for all of the rpmsg sending API. + * + * It will send @data of length @len to @dst, and say it's from @src. The + * message will be sent to the remote processor which the @rpdev channel + * belongs to. + * + * The message is sent using one of the TX buffers that are available for + * communication with this remote processor. + * + * If @wait is true, the caller will be blocked until either a TX buffer is + * available, or 15 seconds elapses (we don't want callers to + * sleep indefinitely due to misbehaving remote processors), and in that + * case -ERESTARTSYS is returned. The number '15' itself was picked + * arbitrarily; there's little point in asking drivers to provide a timeout + * value themselves. + * + * Otherwise, if @wait is false, and there are no TX buffers available, + * the function will immediately fail, and -ENOMEM will be returned. + * + * Normally drivers shouldn't use this function directly; instead, drivers + * should use the appropriate rpmsg_{try}send{to, _offchannel} API + * (see include/linux/rpmsg.h). + * + * Returns 0 on success and an appropriate error value on failure. + */ +int rpmsg_send_offchannel_raw(struct rpmsg_channel *rpdev, u32 src, u32 dst, + void *data, int len, bool wait) +{ + struct virtproc_info *vrp = rpdev->vrp; + struct device *dev = &rpdev->dev; + struct scatterlist sg; + struct rpmsg_hdr *msg; + int err; + + /* bcasting isn't allowed */ + if (src == RPMSG_ADDR_ANY || dst == RPMSG_ADDR_ANY) { + dev_err(dev, "invalid addr (src 0x%x, dst 0x%x)\n", src, dst); + return -EINVAL; + } + + /* + * We currently use fixed-sized buffers, and therefore the payload + * length is limited. + * + * One of the possible improvements here is either to support + * user-provided buffers (and then we can also support zero-copy + * messaging), or to improve the buffer allocator, to support + * variable-length buffer sizes. + */ + if (len > RPMSG_BUF_SIZE - sizeof(struct rpmsg_hdr)) { + dev_err(dev, "message is too big (%d)\n", len); + return -EMSGSIZE; + } + + /* grab a buffer */ + msg = get_a_tx_buf(vrp); + if (!msg && !wait) + return -ENOMEM; + + /* no free buffer ? wait for one (but bail after 15 seconds) */ + while (!msg) { + /* enable "tx-complete" interrupts, if not already enabled */ + rpmsg_upref_sleepers(vrp); + + /* + * sleep until a free buffer is available or 15 secs elapse. + * the timeout period is not configurable because there's + * little point in asking drivers to specify that. + * if later this happens to be required, it'd be easy to add. + */ + err = wait_event_interruptible_timeout(vrp->sendq, + (msg = get_a_tx_buf(vrp)), + msecs_to_jiffies(15000)); + + /* disable "tx-complete" interrupts if we're the last sleeper */ + rpmsg_downref_sleepers(vrp); + + /* timeout ? */ + if (!err) { + dev_err(dev, "timeout waiting for a tx buffer\n"); + return -ERESTARTSYS; + } + } + + msg->len = len; + msg->flags = 0; + msg->src = src; + msg->dst = dst; + msg->reserved = 0; + memcpy(msg->data, data, len); + + dev_dbg(dev, "TX From 0x%x, To 0x%x, Len %d, Flags %d, Reserved %d\n", + msg->src, msg->dst, msg->len, + msg->flags, msg->reserved); + print_hex_dump(KERN_DEBUG, "rpmsg_virtio TX: ", DUMP_PREFIX_NONE, 16, 1, + msg, sizeof(*msg) + msg->len, true); + + sg_init_one(&sg, msg, sizeof(*msg) + len); + + mutex_lock(&vrp->tx_lock); + + /* add message to the remote processor's virtqueue */ + err = virtqueue_add_buf_gfp(vrp->svq, &sg, 1, 0, msg, GFP_KERNEL); + if (err < 0) { + /* + * need to reclaim the buffer here, otherwise it's lost + * (memory won't leak, but rpmsg won't use it again for TX). + * this will wait for a buffer management overhaul. + */ + dev_err(dev, "virtqueue_add_buf_gfp failed: %d\n", err); + goto out; + } + + /* tell the remote processor it has a pending message to read */ + virtqueue_kick(vrp->svq); + + err = 0; +out: + mutex_unlock(&vrp->tx_lock); + return err; +} +EXPORT_SYMBOL(rpmsg_send_offchannel_raw); + +/* called when an rx buffer is used, and it's time to digest a message */ +static void rpmsg_recv_done(struct virtqueue *rvq) +{ + struct rpmsg_hdr *msg; + unsigned int len; + struct rpmsg_endpoint *ept; + struct scatterlist sg; + struct virtproc_info *vrp = rvq->vdev->priv; + struct device *dev = &rvq->vdev->dev; + int err; + + msg = virtqueue_get_buf(rvq, &len); + if (!msg) { + dev_err(dev, "uhm, incoming signal, but no used buffer ?\n"); + return; + } + + dev_dbg(dev, "From: 0x%x, To: 0x%x, Len: %d, Flags: %d, Reserved: %d\n", + msg->src, msg->dst, msg->len, + msg->flags, msg->reserved); + print_hex_dump(KERN_DEBUG, "rpmsg_virtio RX: ", DUMP_PREFIX_NONE, 16, 1, + msg, sizeof(*msg) + msg->len, true); + + /* use the dst addr to fetch the callback of the appropriate user */ + mutex_lock(&vrp->endpoints_lock); + ept = idr_find(&vrp->endpoints, msg->dst); + mutex_unlock(&vrp->endpoints_lock); + + if (ept && ept->cb) + ept->cb(ept->rpdev, msg->data, msg->len, ept->priv, msg->src); + else + dev_warn(dev, "msg received with no recepient\n"); + + sg_init_one(&sg, msg, sizeof(*msg) + len); + + /* add the buffer back to the remote processor's virtqueue */ + err = virtqueue_add_buf_gfp(vrp->rvq, &sg, 0, 1, msg, GFP_KERNEL); + if (err < 0) { + dev_err(dev, "failed to add a virtqueue buffer: %d\n", err); + return; + } + + /* tell the remote processor we added another available rx buffer */ + virtqueue_kick(vrp->rvq); +} + +/* + * This is invoked whenever the remote processor completed processing + * a TX msg we just sent it, and the buffer is put back to the used ring. + * + * Normally, though, we suppress this "tx complete" interrupt in order to + * avoid the incurred overhead. + */ +static void rpmsg_xmit_done(struct virtqueue *svq) +{ + struct virtproc_info *vrp = svq->vdev->priv; + + dev_dbg(&svq->vdev->dev, "%s\n", __func__); + + /* wake up potential senders that are waiting for a tx buffer */ + wake_up_interruptible(&vrp->sendq); +} + +/* invoked when a name service announcement arrives */ +static void rpmsg_ns_cb(struct rpmsg_channel *rpdev, void *data, int len, + void *priv, u32 src) +{ + struct rpmsg_ns_msg *msg = data; + struct rpmsg_channel *newch; + struct rpmsg_channel_info chinfo; + struct virtproc_info *vrp = priv; + struct device *dev = &vrp->vdev->dev; + int ret; + + print_hex_dump(KERN_DEBUG, "NS announcement: ", + DUMP_PREFIX_NONE, 16, 1, + data, len, true); + + if (len != sizeof(*msg)) { + dev_err(dev, "malformed ns msg (%d)\n", len); + return; + } + + /* + * the name service ept does _not_ belong to a real rpmsg channel, + * and is handled by the rpmsg bus itself. + * for sanity reasons, make sure a valid rpdev has _not_ sneaked + * in somehow. + */ + if (rpdev) { + dev_err(dev, "anomaly: ns ept has an rpdev handle\n"); + return; + } + + /* don't trust the remote processor for null terminating the name */ + msg->name[RPMSG_NAME_SIZE - 1] = '\0'; + + dev_info(dev, "%sing channel %s addr 0x%x\n", + msg->flags & RPMSG_NS_DESTROY ? "destroy" : "creat", + msg->name, msg->addr); + + strncpy(chinfo.name, msg->name, sizeof(chinfo.name)); + chinfo.src = RPMSG_ADDR_ANY; + chinfo.dst = msg->addr; + + if (msg->flags & RPMSG_NS_DESTROY) { + ret = rpmsg_destroy_channel(vrp, &chinfo); + if (ret) + dev_err(dev, "rpmsg_destroy_channel failed: %d\n", ret); + } else { + newch = rpmsg_create_channel(vrp, &chinfo); + if (!newch) + dev_err(dev, "rpmsg_create_channel failed\n"); + } +} + +static int rpmsg_probe(struct virtio_device *vdev) +{ + vq_callback_t *vq_cbs[] = { rpmsg_recv_done, rpmsg_xmit_done }; + const char *names[] = { "input", "output" }; + struct virtqueue *vqs[2]; + struct virtproc_info *vrp; + void *bufs_va; + int err = 0, i; + + vrp = kzalloc(sizeof(*vrp), GFP_KERNEL); + if (!vrp) + return -ENOMEM; + + vrp->vdev = vdev; + + idr_init(&vrp->endpoints); + mutex_init(&vrp->endpoints_lock); + mutex_init(&vrp->tx_lock); + init_waitqueue_head(&vrp->sendq); + + /* We expect two virtqueues, rx and tx (and in this order) */ + err = vdev->config->find_vqs(vdev, 2, vqs, vq_cbs, names); + if (err) + goto free_vrp; + + vrp->rvq = vqs[0]; + vrp->svq = vqs[1]; + + /* allocate coherent memory for the buffers */ + bufs_va = dma_alloc_coherent(vdev->dev.parent, RPMSG_TOTAL_BUF_SPACE, + &vrp->bufs_dma, GFP_KERNEL); + if (!bufs_va) + goto vqs_del; + + dev_dbg(&vdev->dev, "buffers: va %p, dma 0x%x\n", bufs_va, + vrp->bufs_dma); + + /* half of the buffers is dedicated for RX */ + vrp->rbufs = bufs_va; + + /* and half is dedicated for TX */ + vrp->sbufs = bufs_va + RPMSG_TOTAL_BUF_SPACE / 2; + + /* set up the receive buffers */ + for (i = 0; i < RPMSG_NUM_BUFS / 2; i++) { + struct scatterlist sg; + void *cpu_addr = vrp->rbufs + i * RPMSG_BUF_SIZE; + + sg_init_one(&sg, cpu_addr, RPMSG_BUF_SIZE); + + err = virtqueue_add_buf_gfp(vrp->rvq, &sg, 0, 1, cpu_addr, + GFP_KERNEL); + WARN_ON(err < 0); /* sanity check; this can't really happen */ + } + + /* suppress "tx-complete" interrupts */ + virtqueue_disable_cb(vrp->svq); + + vdev->priv = vrp; + + /* if supported by the remote processor, enable the name service */ + if (virtio_has_feature(vdev, VIRTIO_RPMSG_F_NS)) { + /* a dedicated endpoint handles the name service msgs */ + vrp->ns_ept = __rpmsg_create_ept(vrp, NULL, rpmsg_ns_cb, + vrp, RPMSG_NS_ADDR); + if (!vrp->ns_ept) { + dev_err(&vdev->dev, "failed to create the ns ept\n"); + err = -ENOMEM; + goto free_coherent; + } + } + + /* tell the remote processor it can start sending messages */ + virtqueue_kick(vrp->rvq); + + dev_info(&vdev->dev, "rpmsg host is online\n"); + + return 0; + +free_coherent: + dma_free_coherent(vdev->dev.parent, RPMSG_TOTAL_BUF_SPACE, bufs_va, + vrp->bufs_dma); +vqs_del: + vdev->config->del_vqs(vrp->vdev); +free_vrp: + kfree(vrp); + return err; +} + +static int rpmsg_remove_device(struct device *dev, void *data) +{ + device_unregister(dev); + + return 0; +} + +static void __devexit rpmsg_remove(struct virtio_device *vdev) +{ + struct virtproc_info *vrp = vdev->priv; + int ret; + + vdev->config->reset(vdev); + + ret = device_for_each_child(&vdev->dev, NULL, rpmsg_remove_device); + if (ret) + dev_warn(&vdev->dev, "can't remove rpmsg device: %d\n", ret); + + idr_remove_all(&vrp->endpoints); + idr_destroy(&vrp->endpoints); + + vdev->config->del_vqs(vrp->vdev); + + dma_free_coherent(vdev->dev.parent, RPMSG_TOTAL_BUF_SPACE, + vrp->rbufs, vrp->bufs_dma); + + kfree(vrp); +} + +static struct virtio_device_id id_table[] = { + { VIRTIO_ID_RPMSG, VIRTIO_DEV_ANY_ID }, + { 0 }, +}; + +static unsigned int features[] = { + VIRTIO_RPMSG_F_NS, +}; + +static struct virtio_driver virtio_ipc_driver = { + .feature_table = features, + .feature_table_size = ARRAY_SIZE(features), + .driver.name = KBUILD_MODNAME, + .driver.owner = THIS_MODULE, + .id_table = id_table, + .probe = rpmsg_probe, + .remove = __devexit_p(rpmsg_remove), +}; + +static int __init rpmsg_init(void) +{ + int ret; + + ret = bus_register(&rpmsg_bus); + if (ret) { + pr_err("failed to register rpmsg bus: %d\n", ret); + return ret; + } + + ret = register_virtio_driver(&virtio_ipc_driver); + if (ret) { + pr_err("failed to register virtio driver: %d\n", ret); + bus_unregister(&rpmsg_bus); + } + + return ret; +} +module_init(rpmsg_init); + +static void __exit rpmsg_fini(void) +{ + unregister_virtio_driver(&virtio_ipc_driver); + bus_unregister(&rpmsg_bus); +} +module_exit(rpmsg_fini); + +MODULE_DEVICE_TABLE(virtio, id_table); +MODULE_DESCRIPTION("Virtio-based remote processor messaging bus"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h index b29e7f6f8fa5..92aef8aaef1a 100644 --- a/include/linux/mod_devicetable.h +++ b/include/linux/mod_devicetable.h @@ -414,6 +414,15 @@ struct hv_vmbus_device_id { __attribute__((aligned(sizeof(kernel_ulong_t)))); }; +/* rpmsg */ + +#define RPMSG_NAME_SIZE 32 +#define RPMSG_DEVICE_MODALIAS_FMT "rpmsg:%s" + +struct rpmsg_device_id { + char name[RPMSG_NAME_SIZE]; +}; + /* i2c */ #define I2C_NAME_SIZE 20 diff --git a/include/linux/rpmsg.h b/include/linux/rpmsg.h new file mode 100644 index 000000000000..a8e50e44203c --- /dev/null +++ b/include/linux/rpmsg.h @@ -0,0 +1,326 @@ +/* + * Remote processor messaging + * + * Copyright (C) 2011 Texas Instruments, Inc. + * Copyright (C) 2011 Google, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Texas Instruments nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _LINUX_RPMSG_H +#define _LINUX_RPMSG_H + +#include +#include +#include + +/* The feature bitmap for virtio rpmsg */ +#define VIRTIO_RPMSG_F_NS 0 /* RP supports name service notifications */ + +/** + * struct rpmsg_hdr - common header for all rpmsg messages + * @src: source address + * @dst: destination address + * @reserved: reserved for future use + * @len: length of payload (in bytes) + * @flags: message flags + * @data: @len bytes of message payload data + * + * Every message sent(/received) on the rpmsg bus begins with this header. + */ +struct rpmsg_hdr { + u32 src; + u32 dst; + u32 reserved; + u16 len; + u16 flags; + u8 data[0]; +} __packed; + +/** + * struct rpmsg_ns_msg - dynamic name service announcement message + * @name: name of remote service that is published + * @addr: address of remote service that is published + * @flags: indicates whether service is created or destroyed + * + * This message is sent across to publish a new service, or announce + * about its removal. When we receive these messages, an appropriate + * rpmsg channel (i.e device) is created/destroyed. In turn, the ->probe() + * or ->remove() handler of the appropriate rpmsg driver will be invoked + * (if/as-soon-as one is registered). + */ +struct rpmsg_ns_msg { + char name[RPMSG_NAME_SIZE]; + u32 addr; + u32 flags; +} __packed; + +/** + * enum rpmsg_ns_flags - dynamic name service announcement flags + * + * @RPMSG_NS_CREATE: a new remote service was just created + * @RPMSG_NS_DESTROY: a known remote service was just destroyed + */ +enum rpmsg_ns_flags { + RPMSG_NS_CREATE = 0, + RPMSG_NS_DESTROY = 1, +}; + +#define RPMSG_ADDR_ANY 0xFFFFFFFF + +struct virtproc_info; + +/** + * rpmsg_channel - devices that belong to the rpmsg bus are called channels + * @vrp: the remote processor this channel belongs to + * @dev: the device struct + * @id: device id (used to match between rpmsg drivers and devices) + * @src: local address + * @dst: destination address + * @ept: the rpmsg endpoint of this channel + * @announce: if set, rpmsg will announce the creation/removal of this channel + */ +struct rpmsg_channel { + struct virtproc_info *vrp; + struct device dev; + struct rpmsg_device_id id; + u32 src; + u32 dst; + struct rpmsg_endpoint *ept; + bool announce; +}; + +typedef void (*rpmsg_rx_cb_t)(struct rpmsg_channel *, void *, int, void *, u32); + +/** + * struct rpmsg_endpoint - binds a local rpmsg address to its user + * @rpdev: rpmsg channel device + * @cb: rx callback handler + * @addr: local rpmsg address + * @priv: private data for the driver's use + * + * In essence, an rpmsg endpoint represents a listener on the rpmsg bus, as + * it binds an rpmsg address with an rx callback handler. + * + * Simple rpmsg drivers shouldn't use this struct directly, because + * things just work: every rpmsg driver provides an rx callback upon + * registering to the bus, and that callback is then bound to its rpmsg + * address when the driver is probed. When relevant inbound messages arrive + * (i.e. messages which their dst address equals to the src address of + * the rpmsg channel), the driver's handler is invoked to process it. + * + * More complicated drivers though, that do need to allocate additional rpmsg + * addresses, and bind them to different rx callbacks, must explicitly + * create additional endpoints by themselves (see rpmsg_create_ept()). + */ +struct rpmsg_endpoint { + struct rpmsg_channel *rpdev; + rpmsg_rx_cb_t cb; + u32 addr; + void *priv; +}; + +/** + * struct rpmsg_driver - rpmsg driver struct + * @drv: underlying device driver + * @id_table: rpmsg ids serviced by this driver + * @probe: invoked when a matching rpmsg channel (i.e. device) is found + * @remove: invoked when the rpmsg channel is removed + * @callback: invoked when an inbound message is received on the channel + */ +struct rpmsg_driver { + struct device_driver drv; + const struct rpmsg_device_id *id_table; + int (*probe)(struct rpmsg_channel *dev); + void (*remove)(struct rpmsg_channel *dev); + void (*callback)(struct rpmsg_channel *, void *, int, void *, u32); +}; + +int register_rpmsg_device(struct rpmsg_channel *dev); +void unregister_rpmsg_device(struct rpmsg_channel *dev); +int register_rpmsg_driver(struct rpmsg_driver *drv); +void unregister_rpmsg_driver(struct rpmsg_driver *drv); +void rpmsg_destroy_ept(struct rpmsg_endpoint *); +struct rpmsg_endpoint *rpmsg_create_ept(struct rpmsg_channel *, + rpmsg_rx_cb_t cb, void *priv, u32 addr); +int +rpmsg_send_offchannel_raw(struct rpmsg_channel *, u32, u32, void *, int, bool); + +/** + * rpmsg_send() - send a message across to the remote processor + * @rpdev: the rpmsg channel + * @data: payload of message + * @len: length of payload + * + * This function sends @data of length @len on the @rpdev channel. + * The message will be sent to the remote processor which the @rpdev + * channel belongs to, using @rpdev's source and destination addresses. + * In case there are no TX buffers available, the function will block until + * one becomes available, or a timeout of 15 seconds elapses. When the latter + * happens, -ERESTARTSYS is returned. + * + * Can only be called from process context (for now). + * + * Returns 0 on success and an appropriate error value on failure. + */ +static inline int rpmsg_send(struct rpmsg_channel *rpdev, void *data, int len) +{ + u32 src = rpdev->src, dst = rpdev->dst; + + return rpmsg_send_offchannel_raw(rpdev, src, dst, data, len, true); +} + +/** + * rpmsg_sendto() - send a message across to the remote processor, specify dst + * @rpdev: the rpmsg channel + * @data: payload of message + * @len: length of payload + * @dst: destination address + * + * This function sends @data of length @len to the remote @dst address. + * The message will be sent to the remote processor which the @rpdev + * channel belongs to, using @rpdev's source address. + * In case there are no TX buffers available, the function will block until + * one becomes available, or a timeout of 15 seconds elapses. When the latter + * happens, -ERESTARTSYS is returned. + * + * Can only be called from process context (for now). + * + * Returns 0 on success and an appropriate error value on failure. + */ +static inline +int rpmsg_sendto(struct rpmsg_channel *rpdev, void *data, int len, u32 dst) +{ + u32 src = rpdev->src; + + return rpmsg_send_offchannel_raw(rpdev, src, dst, data, len, true); +} + +/** + * rpmsg_send_offchannel() - send a message using explicit src/dst addresses + * @rpdev: the rpmsg channel + * @src: source address + * @dst: destination address + * @data: payload of message + * @len: length of payload + * + * This function sends @data of length @len to the remote @dst address, + * and uses @src as the source address. + * The message will be sent to the remote processor which the @rpdev + * channel belongs to. + * In case there are no TX buffers available, the function will block until + * one becomes available, or a timeout of 15 seconds elapses. When the latter + * happens, -ERESTARTSYS is returned. + * + * Can only be called from process context (for now). + * + * Returns 0 on success and an appropriate error value on failure. + */ +static inline +int rpmsg_send_offchannel(struct rpmsg_channel *rpdev, u32 src, u32 dst, + void *data, int len) +{ + return rpmsg_send_offchannel_raw(rpdev, src, dst, data, len, true); +} + +/** + * rpmsg_send() - send a message across to the remote processor + * @rpdev: the rpmsg channel + * @data: payload of message + * @len: length of payload + * + * This function sends @data of length @len on the @rpdev channel. + * The message will be sent to the remote processor which the @rpdev + * channel belongs to, using @rpdev's source and destination addresses. + * In case there are no TX buffers available, the function will immediately + * return -ENOMEM without waiting until one becomes available. + * + * Can only be called from process context (for now). + * + * Returns 0 on success and an appropriate error value on failure. + */ +static inline +int rpmsg_trysend(struct rpmsg_channel *rpdev, void *data, int len) +{ + u32 src = rpdev->src, dst = rpdev->dst; + + return rpmsg_send_offchannel_raw(rpdev, src, dst, data, len, false); +} + +/** + * rpmsg_sendto() - send a message across to the remote processor, specify dst + * @rpdev: the rpmsg channel + * @data: payload of message + * @len: length of payload + * @dst: destination address + * + * This function sends @data of length @len to the remote @dst address. + * The message will be sent to the remote processor which the @rpdev + * channel belongs to, using @rpdev's source address. + * In case there are no TX buffers available, the function will immediately + * return -ENOMEM without waiting until one becomes available. + * + * Can only be called from process context (for now). + * + * Returns 0 on success and an appropriate error value on failure. + */ +static inline +int rpmsg_trysendto(struct rpmsg_channel *rpdev, void *data, int len, u32 dst) +{ + u32 src = rpdev->src; + + return rpmsg_send_offchannel_raw(rpdev, src, dst, data, len, false); +} + +/** + * rpmsg_send_offchannel() - send a message using explicit src/dst addresses + * @rpdev: the rpmsg channel + * @src: source address + * @dst: destination address + * @data: payload of message + * @len: length of payload + * + * This function sends @data of length @len to the remote @dst address, + * and uses @src as the source address. + * The message will be sent to the remote processor which the @rpdev + * channel belongs to. + * In case there are no TX buffers available, the function will immediately + * return -ENOMEM without waiting until one becomes available. + * + * Can only be called from process context (for now). + * + * Returns 0 on success and an appropriate error value on failure. + */ +static inline +int rpmsg_trysend_offchannel(struct rpmsg_channel *rpdev, u32 src, u32 dst, + void *data, int len) +{ + return rpmsg_send_offchannel_raw(rpdev, src, dst, data, len, false); +} + +#endif /* _LINUX_RPMSG_H */ diff --git a/include/linux/virtio_ids.h b/include/linux/virtio_ids.h index 85bb0bb66ffc..b37c5212265e 100644 --- a/include/linux/virtio_ids.h +++ b/include/linux/virtio_ids.h @@ -34,6 +34,7 @@ #define VIRTIO_ID_CONSOLE 3 /* virtio console */ #define VIRTIO_ID_RNG 4 /* virtio ring */ #define VIRTIO_ID_BALLOON 5 /* virtio balloon */ +#define VIRTIO_ID_RPMSG 7 /* virtio remote processor messaging */ #define VIRTIO_ID_9P 9 /* 9p virtio console */ #endif /* _LINUX_VIRTIO_IDS_H */ -- cgit v1.2.3 From 2fd51811b8b87408fd680b442364e3474a1a0f21 Mon Sep 17 00:00:00 2001 From: Ohad Ben-Cohen Date: Tue, 13 Dec 2011 12:17:59 +0200 Subject: remoteproc: remove unused resource type RSC_VIRTIO_CFG isn't being used, so remove it. Originally it was introduced to overcome a resource table limitation that prevented describing a virtio device in a single resource table entry. The plan though is to describe resource table entries in a TLV fashion, where each entry will consume the amount of space it requires, so the original limitation is anyway temporary. Reported-by: Stephen Boyd Signed-off-by: Ohad Ben-Cohen --- include/linux/remoteproc.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h index 1edbfde4593c..b52f78413c5c 100644 --- a/include/linux/remoteproc.h +++ b/include/linux/remoteproc.h @@ -122,7 +122,6 @@ enum fw_resource_type { RSC_TRACE = 2, RSC_VRING = 3, RSC_VIRTIO_DEV = 4, - RSC_VIRTIO_CFG = 5, }; /** -- cgit v1.2.3 From 1cf0c6e69e396538615153056605aaafab11935a Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Mon, 6 Feb 2012 08:49:25 +0100 Subject: Input: Add EVIOC mechanism for MT slots This patch adds the ability to extract MT slot data via a new ioctl, EVIOCGMTSLOTS. The function returns an array of slot values for the specified ABS_MT event type. Example of user space usage: struct { unsigned code; int values[64]; } req; req.code = ABS_MT_POSITION_X; if (ioctl(fd, EVIOCGMTSLOTS(sizeof(req)), &req) < 0) return -1; for (i = 0; i < 64; i++) printf("slot %d: %d\n", i, req.values[i]); Reviewed-by: Chase Douglas Signed-off-by: Henrik Rydberg --- drivers/input/evdev.c | 27 ++++++++++++++++++++++++++- include/linux/input.h | 25 +++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index 76457d50bc34..e4cad161be9d 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include #include #include "input-compat.h" @@ -623,6 +623,28 @@ static int evdev_handle_set_keycode_v2(struct input_dev *dev, void __user *p) return input_set_keycode(dev, &ke); } +static int evdev_handle_mt_request(struct input_dev *dev, + unsigned int size, + int __user *ip) +{ + const struct input_mt_slot *mt = dev->mt; + unsigned int code; + int max_slots; + int i; + + if (get_user(code, &ip[0])) + return -EFAULT; + if (!input_is_mt_value(code)) + return -EINVAL; + + max_slots = (size - sizeof(__u32)) / sizeof(__s32); + for (i = 0; i < dev->mtsize && i < max_slots; i++) + if (put_user(input_mt_get_value(&mt[i], code), &ip[1 + i])) + return -EFAULT; + + return 0; +} + static long evdev_do_ioctl(struct file *file, unsigned int cmd, void __user *p, int compat_mode) { @@ -708,6 +730,9 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd, return bits_to_user(dev->propbit, INPUT_PROP_MAX, size, p, compat_mode); + case EVIOCGMTSLOTS(0): + return evdev_handle_mt_request(dev, size, ip); + case EVIOCGKEY(0): return bits_to_user(dev->key, KEY_MAX, size, p, compat_mode); diff --git a/include/linux/input.h b/include/linux/input.h index 3862e32c4eeb..af264438631d 100644 --- a/include/linux/input.h +++ b/include/linux/input.h @@ -114,6 +114,31 @@ struct input_keymap_entry { #define EVIOCGUNIQ(len) _IOC(_IOC_READ, 'E', 0x08, len) /* get unique identifier */ #define EVIOCGPROP(len) _IOC(_IOC_READ, 'E', 0x09, len) /* get device properties */ +/** + * EVIOCGMTSLOTS(len) - get MT slot values + * + * The ioctl buffer argument should be binary equivalent to + * + * struct input_mt_request_layout { + * __u32 code; + * __s32 values[num_slots]; + * }; + * + * where num_slots is the (arbitrary) number of MT slots to extract. + * + * The ioctl size argument (len) is the size of the buffer, which + * should satisfy len = (num_slots + 1) * sizeof(__s32). If len is + * too small to fit all available slots, the first num_slots are + * returned. + * + * Before the call, code is set to the wanted ABS_MT event type. On + * return, values[] is filled with the slot values for the specified + * ABS_MT code. + * + * If the request code is not an ABS_MT value, -EINVAL is returned. + */ +#define EVIOCGMTSLOTS(len) _IOC(_IOC_READ, 'E', 0x0a, len) + #define EVIOCGKEY(len) _IOC(_IOC_READ, 'E', 0x18, len) /* get global key state */ #define EVIOCGLED(len) _IOC(_IOC_READ, 'E', 0x19, len) /* get all LEDs */ #define EVIOCGSND(len) _IOC(_IOC_READ, 'E', 0x1a, len) /* get all sounds status */ -- cgit v1.2.3 From 28a8d14cc74a0180323d9150c3d3dbf9dd60d55a Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 9 Feb 2012 01:52:22 +0100 Subject: pinctrl: break out a pinctrl consumer header This breaks out a header to be used by all pinmux and pinconfig alike, so drivers needing services from pinctrl does not need to include different headers. This is similar to the approach taken by the regulator API. Acked-by: Stephen Warren Signed-off-by: Linus Walleij --- Documentation/pinctrl.txt | 6 +- arch/arm/mach-u300/core.c | 2 +- drivers/pinctrl/pinctrl-coh901.c | 2 +- include/linux/pinctrl/consumer.h | 118 +++++++++++++++++++++++++++++++++++++++ include/linux/pinctrl/pinconf.h | 39 ------------- include/linux/pinctrl/pinmux.h | 52 ----------------- 6 files changed, 125 insertions(+), 94 deletions(-) create mode 100644 include/linux/pinctrl/consumer.h (limited to 'include/linux') diff --git a/Documentation/pinctrl.txt b/Documentation/pinctrl.txt index 14aecd2b2dbc..b268832c49d2 100644 --- a/Documentation/pinctrl.txt +++ b/Documentation/pinctrl.txt @@ -208,6 +208,8 @@ unconnected. For example, a platform may do this: +#include + ret = pin_config_set("foo-dev", "FOO_GPIO_PIN", PLATFORM_X_PULL_UP); To pull up a pin to VDD. The pin configuration driver implements callbacks for @@ -920,7 +922,7 @@ this is not possible. A driver may request a certain mux to be activated, usually just the default mux like this: -#include +#include struct foo_state { struct pinmux *pmx; @@ -1019,6 +1021,8 @@ function, but with different named in the mapping as described under This snippet first muxes the function in the pins defined by group A, enables it, disables and releases it, and muxes it in on the pins defined by group B: +#include + foo_switch() { struct pinmux *pmx; diff --git a/arch/arm/mach-u300/core.c b/arch/arm/mach-u300/core.c index b4c6926a700c..1429e5282198 100644 --- a/arch/arm/mach-u300/core.c +++ b/arch/arm/mach-u300/core.c @@ -26,7 +26,7 @@ #include #include #include -#include +#include #include #include diff --git a/drivers/pinctrl/pinctrl-coh901.c b/drivers/pinctrl/pinctrl-coh901.c index 69fb7072a23e..8bac3f093d58 100644 --- a/drivers/pinctrl/pinctrl-coh901.c +++ b/drivers/pinctrl/pinctrl-coh901.c @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include /* diff --git a/include/linux/pinctrl/consumer.h b/include/linux/pinctrl/consumer.h new file mode 100644 index 000000000000..9c8513d5d0fb --- /dev/null +++ b/include/linux/pinctrl/consumer.h @@ -0,0 +1,118 @@ +/* + * Consumer interface the pin control subsystem + * + * Copyright (C) 2012 ST-Ericsson SA + * Written on behalf of Linaro for ST-Ericsson + * Based on bits of regulator core, gpio core and clk core + * + * Author: Linus Walleij + * + * License terms: GNU General Public License (GPL) version 2 + */ +#ifndef __LINUX_PINCTRL_CONSUMER_H +#define __LINUX_PINCTRL_CONSUMER_H + +#include +#include +#include "pinctrl.h" + +/* This struct is private to the core and should be regarded as a cookie */ +struct pinmux; + +#ifdef CONFIG_PINMUX + +/* External interface to pinmux */ +extern int pinmux_request_gpio(unsigned gpio); +extern void pinmux_free_gpio(unsigned gpio); +extern int pinmux_gpio_direction_input(unsigned gpio); +extern int pinmux_gpio_direction_output(unsigned gpio); +extern struct pinmux * __must_check pinmux_get(struct device *dev, const char *name); +extern void pinmux_put(struct pinmux *pmx); +extern int pinmux_enable(struct pinmux *pmx); +extern void pinmux_disable(struct pinmux *pmx); + +#else /* !CONFIG_PINMUX */ + +static inline int pinmux_request_gpio(unsigned gpio) +{ + return 0; +} + +static inline void pinmux_free_gpio(unsigned gpio) +{ +} + +static inline int pinmux_gpio_direction_input(unsigned gpio) +{ + return 0; +} + +static inline int pinmux_gpio_direction_output(unsigned gpio) +{ + return 0; +} + +static inline struct pinmux * __must_check pinmux_get(struct device *dev, const char *name) +{ + return NULL; +} + +static inline void pinmux_put(struct pinmux *pmx) +{ +} + +static inline int pinmux_enable(struct pinmux *pmx) +{ + return 0; +} + +static inline void pinmux_disable(struct pinmux *pmx) +{ +} + +#endif /* CONFIG_PINMUX */ + +#ifdef CONFIG_PINCONF + +extern int pin_config_get(const char *dev_name, const char *name, + unsigned long *config); +extern int pin_config_set(const char *dev_name, const char *name, + unsigned long config); +extern int pin_config_group_get(const char *dev_name, + const char *pin_group, + unsigned long *config); +extern int pin_config_group_set(const char *dev_name, + const char *pin_group, + unsigned long config); + +#else + +static inline int pin_config_get(const char *dev_name, const char *name, + unsigned long *config) +{ + return 0; +} + +static inline int pin_config_set(const char *dev_name, const char *name, + unsigned long config) +{ + return 0; +} + +static inline int pin_config_group_get(const char *dev_name, + const char *pin_group, + unsigned long *config) +{ + return 0; +} + +static inline int pin_config_group_set(const char *dev_name, + const char *pin_group, + unsigned long config) +{ + return 0; +} + +#endif + +#endif /* __LINUX_PINCTRL_CONSUMER_H */ diff --git a/include/linux/pinctrl/pinconf.h b/include/linux/pinctrl/pinconf.h index 477922cf043a..f8cf156873d7 100644 --- a/include/linux/pinctrl/pinconf.h +++ b/include/linux/pinctrl/pinconf.h @@ -53,45 +53,6 @@ struct pinconf_ops { unsigned selector); }; -extern int pin_config_get(const char *dev_name, const char *name, - unsigned long *config); -extern int pin_config_set(const char *dev_name, const char *name, - unsigned long config); -extern int pin_config_group_get(const char *dev_name, - const char *pin_group, - unsigned long *config); -extern int pin_config_group_set(const char *dev_name, - const char *pin_group, - unsigned long config); - -#else - -static inline int pin_config_get(const char *dev_name, const char *name, - unsigned long *config) -{ - return 0; -} - -static inline int pin_config_set(const char *dev_name, const char *name, - unsigned long config) -{ - return 0; -} - -static inline int pin_config_group_get(const char *dev_name, - const char *pin_group, - unsigned long *config) -{ - return 0; -} - -static inline int pin_config_group_set(const char *dev_name, - const char *pin_group, - unsigned long config) -{ - return 0; -} - #endif #endif /* __LINUX_PINCTRL_PINCONF_H */ diff --git a/include/linux/pinctrl/pinmux.h b/include/linux/pinctrl/pinmux.h index 937b3e2fa36f..47e9237edd47 100644 --- a/include/linux/pinctrl/pinmux.h +++ b/include/linux/pinctrl/pinmux.h @@ -16,9 +16,6 @@ #include #include "pinctrl.h" -/* This struct is private to the core and should be regarded as a cookie */ -struct pinmux; - #ifdef CONFIG_PINMUX struct pinctrl_dev; @@ -88,55 +85,6 @@ struct pinmux_ops { bool input); }; -/* External interface to pinmux */ -extern int pinmux_request_gpio(unsigned gpio); -extern void pinmux_free_gpio(unsigned gpio); -extern int pinmux_gpio_direction_input(unsigned gpio); -extern int pinmux_gpio_direction_output(unsigned gpio); -extern struct pinmux * __must_check pinmux_get(struct device *dev, const char *name); -extern void pinmux_put(struct pinmux *pmx); -extern int pinmux_enable(struct pinmux *pmx); -extern void pinmux_disable(struct pinmux *pmx); - -#else /* !CONFIG_PINMUX */ - -static inline int pinmux_request_gpio(unsigned gpio) -{ - return 0; -} - -static inline void pinmux_free_gpio(unsigned gpio) -{ -} - -static inline int pinmux_gpio_direction_input(unsigned gpio) -{ - return 0; -} - -static inline int pinmux_gpio_direction_output(unsigned gpio) -{ - return 0; -} - -static inline struct pinmux * __must_check pinmux_get(struct device *dev, const char *name) -{ - return NULL; -} - -static inline void pinmux_put(struct pinmux *pmx) -{ -} - -static inline int pinmux_enable(struct pinmux *pmx) -{ - return 0; -} - -static inline void pinmux_disable(struct pinmux *pmx) -{ -} - #endif /* CONFIG_PINMUX */ #endif /* __LINUX_PINCTRL_PINMUX_H */ -- cgit v1.2.3 From e93bcee00c43e2bd4037291262111016f4c05793 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 9 Feb 2012 07:23:28 +0100 Subject: pinctrl: move generic functions to the pinctrl_ namespace Since we want to use the former pinmux handles and mapping tables for generic control involving both muxing and configuration we begin refactoring by renaming them from pinmux_* to pinctrl_*. ChangeLog v1->v2: - Also rename the PINMUX_* macros in machine.h to PIN_ as indicated in the documentation so as to reflect the generic nature of these mapping entries from now on. Acked-by: Stephen Warren Signed-off-by: Linus Walleij --- Documentation/pinctrl.txt | 118 ++++++------- arch/arm/mach-u300/core.c | 34 ++-- drivers/pinctrl/core.c | 4 +- drivers/pinctrl/core.h | 8 +- drivers/pinctrl/pinctrl-coh901.c | 4 +- drivers/pinctrl/pinmux.c | 351 ++++++++++++++++++++------------------- drivers/pinctrl/pinmux.h | 8 +- include/linux/pinctrl/consumer.h | 34 ++-- include/linux/pinctrl/machine.h | 26 +-- 9 files changed, 294 insertions(+), 293 deletions(-) (limited to 'include/linux') diff --git a/Documentation/pinctrl.txt b/Documentation/pinctrl.txt index b268832c49d2..2e7132355db8 100644 --- a/Documentation/pinctrl.txt +++ b/Documentation/pinctrl.txt @@ -728,19 +728,19 @@ same time. All the above functions are mandatory to implement for a pinmux driver. -Pinmux interaction with the GPIO subsystem -========================================== +Pin control interaction with the GPIO subsystem +=============================================== -The public pinmux API contains two functions named pinmux_request_gpio() -and pinmux_free_gpio(). These two functions shall *ONLY* be called from +The public pinmux API contains two functions named pinctrl_request_gpio() +and pinctrl_free_gpio(). These two functions shall *ONLY* be called from gpiolib-based drivers as part of their gpio_request() and -gpio_free() semantics. Likewise the pinmux_gpio_direction_[input|output] +gpio_free() semantics. Likewise the pinctrl_gpio_direction_[input|output] shall only be called from within respective gpio_direction_[input|output] gpiolib implementation. NOTE that platforms and individual drivers shall *NOT* request GPIO pins to be -muxed in. Instead, implement a proper gpiolib driver and have that driver -request proper muxing for its pins. +controlled e.g. muxed in. Instead, implement a proper gpiolib driver and have +that driver request proper muxing and other control for its pins. The function list could become long, especially if you can convert every individual pin into a GPIO pin independent of any other pins, and then try @@ -749,7 +749,7 @@ the approach to define every pin as a function. In this case, the function array would become 64 entries for each GPIO setting and then the device functions. -For this reason there are two functions a pinmux driver can implement +For this reason there are two functions a pin control driver can implement to enable only GPIO on an individual pin: .gpio_request_enable() and .gpio_disable_free(). @@ -764,7 +764,7 @@ gpiolib driver and the affected GPIO range, pin offset and desired direction will be passed along to this function. Alternatively to using these special functions, it is fully allowed to use -named functions for each GPIO pin, the pinmux_request_gpio() will attempt to +named functions for each GPIO pin, the pinctrl_request_gpio() will attempt to obtain the function "gpioN" where "N" is the global GPIO pin number if no special GPIO-handler is registered. @@ -783,7 +783,7 @@ spi on the second function mapping: #include -static const struct pinmux_map __initdata pmx_mapping[] = { +static const struct pinctrl_map __initdata mapping[] = { { .ctrl_dev_name = "pinctrl-foo", .function = "spi0", @@ -811,14 +811,14 @@ to map. You register this pinmux mapping to the pinmux subsystem by simply: - ret = pinmux_register_mappings(pmx_mapping, ARRAY_SIZE(pmx_mapping)); + ret = pinctrl_register_mappings(mapping, ARRAY_SIZE(mapping)); Since the above construct is pretty common there is a helper macro to make it even more compact which assumes you want to use pinctrl-foo and position 0 for mapping, for example: -static struct pinmux_map __initdata pmx_mapping[] = { - PINMUX_MAP("I2CMAP", "pinctrl-foo", "i2c0", "foo-i2c.0"), +static struct pinctrl_map __initdata mapping[] = { + PIN_MAP("I2CMAP", "pinctrl-foo", "i2c0", "foo-i2c.0"), }; @@ -901,7 +901,7 @@ case), we define a mapping like this: The result of grabbing this mapping from the device with something like this (see next paragraph): - pmx = pinmux_get(&device, "8bit"); + p = pinctrl_get(&device, "8bit"); Will be that you activate all the three bottom records in the mapping at once. Since they share the same name, pin controller device, funcion and @@ -913,44 +913,44 @@ pinmux core. Pinmux requests from drivers ============================ -Generally it is discouraged to let individual drivers get and enable pinmuxes. -So if possible, handle the pinmuxes in platform code or some other place where -you have access to all the affected struct device * pointers. In some cases -where a driver needs to switch between different mux mappings at runtime -this is not possible. +Generally it is discouraged to let individual drivers get and enable pin +control. So if possible, handle the pin control in platform code or some other +place where you have access to all the affected struct device * pointers. In +some cases where a driver needs to e.g. switch between different mux mappings +at runtime this is not possible. -A driver may request a certain mux to be activated, usually just the default -mux like this: +A driver may request a certain control state to be activated, usually just the +default state like this: #include struct foo_state { - struct pinmux *pmx; + struct pinctrl *p; ... }; foo_probe() { /* Allocate a state holder named "state" etc */ - struct pinmux pmx; + struct pinctrl p; - pmx = pinmux_get(&device, NULL); - if IS_ERR(pmx) - return PTR_ERR(pmx); - pinmux_enable(pmx); + p = pinctrl_get(&device, NULL); + if IS_ERR(p) + return PTR_ERR(p); + pinctrl_enable(p); - state->pmx = pmx; + state->p = p; } foo_remove() { - pinmux_disable(state->pmx); - pinmux_put(state->pmx); + pinctrl_disable(state->p); + pinctrl_put(state->p); } -If you want to grab a specific mux mapping and not just the first one found for -this device you can specify a specific mapping name, for example in the above -example the second i2c0 setting: pinmux_get(&device, "spi0-pos-B"); +If you want to grab a specific control mapping and not just the first one +found for this device you can specify a specific mapping name, for example in +the above example the second i2c0 setting: pinctrl_get(&device, "spi0-pos-B"); This get/enable/disable/put sequence can just as well be handled by bus drivers if you don't want each and every driver to handle it and you know the @@ -958,35 +958,35 @@ arrangement on your bus. The semantics of the get/enable respective disable/put is as follows: -- pinmux_get() is called in process context to reserve the pins affected with +- pinctrl_get() is called in process context to reserve the pins affected with a certain mapping and set up the pinmux core and the driver. It will allocate a struct from the kernel memory to hold the pinmux state. -- pinmux_enable()/pinmux_disable() is quick and can be called from fastpath +- pinctrl_enable()/pinctrl_disable() is quick and can be called from fastpath (irq context) when you quickly want to set up/tear down the hardware muxing when running a device driver. Usually it will just poke some values into a register. -- pinmux_disable() is called in process context to tear down the pin requests - and release the state holder struct for the mux setting. +- pinctrl_disable() is called in process context to tear down the pin requests + and release the state holder struct for the mux setting etc. -Usually the pinmux core handled the get/put pair and call out to the device -drivers bookkeeping operations, like checking available functions and the -associated pins, whereas the enable/disable pass on to the pin controller +Usually the pin control core handled the get/put pair and call out to the +device drivers bookkeeping operations, like checking available functions and +the associated pins, whereas the enable/disable pass on to the pin controller driver which takes care of activating and/or deactivating the mux setting by quickly poking some registers. -The pins are allocated for your device when you issue the pinmux_get() call, +The pins are allocated for your device when you issue the pinctrl_get() call, after this you should be able to see this in the debugfs listing of all pins. -System pinmux hogging -===================== +System pin control hogging +========================== -A system pinmux map entry, i.e. a pinmux setting that does not have a device -associated with it, can be hogged by the core when the pin controller is -registered. This means that the core will attempt to call pinmux_get() and -pinmux_enable() on it immediately after the pin control device has been +A system pin control map entry, i.e. a pin control setting that does not have +a device associated with it, can be hogged by the core when the pin controller +is registered. This means that the core will attempt to call pinctrl_get() and +pinctrl_enable() on it immediately after the pin control device has been registered. This is enabled by simply setting the .hog_on_boot field in the map to true, @@ -1003,7 +1003,7 @@ Since it may be common to request the core to hog a few always-applicable mux settings on the primary pin controller, there is a convenience macro for this: -PINMUX_MAP_PRIMARY_SYS_HOG("POWERMAP", "power_func") +PIN_MAP_PRIMARY_SYS_HOG("POWERMAP", "power_func") This gives the exact same result as the above construction. @@ -1025,23 +1025,23 @@ it, disables and releases it, and muxes it in on the pins defined by group B: foo_switch() { - struct pinmux *pmx; + struct pinctrl *p; /* Enable on position A */ - pmx = pinmux_get(&device, "spi0-pos-A"); - if IS_ERR(pmx) - return PTR_ERR(pmx); - pinmux_enable(pmx); + p = pinctrl_get(&device, "spi0-pos-A"); + if IS_ERR(p) + return PTR_ERR(p); + pinctrl_enable(p); /* This releases the pins again */ - pinmux_disable(pmx); - pinmux_put(pmx); + pinctrl_disable(p); + pinctrl_put(p); /* Enable on position B */ - pmx = pinmux_get(&device, "spi0-pos-B"); - if IS_ERR(pmx) - return PTR_ERR(pmx); - pinmux_enable(pmx); + p = pinctrl_get(&device, "spi0-pos-B"); + if IS_ERR(p) + return PTR_ERR(p); + pinctrl_enable(p); ... } diff --git a/arch/arm/mach-u300/core.c b/arch/arm/mach-u300/core.c index 1429e5282198..bb1034f8c2f5 100644 --- a/arch/arm/mach-u300/core.c +++ b/arch/arm/mach-u300/core.c @@ -1605,21 +1605,21 @@ static struct platform_device pinmux_device = { }; /* Pinmux settings */ -static struct pinmux_map __initdata u300_pinmux_map[] = { +static struct pinctrl_map __initdata u300_pinmux_map[] = { /* anonymous maps for chip power and EMIFs */ - PINMUX_MAP_SYS_HOG("POWER", "pinmux-u300", "power"), - PINMUX_MAP_SYS_HOG("EMIF0", "pinmux-u300", "emif0"), - PINMUX_MAP_SYS_HOG("EMIF1", "pinmux-u300", "emif1"), + PIN_MAP_SYS_HOG("POWER", "pinmux-u300", "power"), + PIN_MAP_SYS_HOG("EMIF0", "pinmux-u300", "emif0"), + PIN_MAP_SYS_HOG("EMIF1", "pinmux-u300", "emif1"), /* per-device maps for MMC/SD, SPI and UART */ - PINMUX_MAP("MMCSD", "pinmux-u300", "mmc0", "mmci"), - PINMUX_MAP("SPI", "pinmux-u300", "spi0", "pl022"), - PINMUX_MAP("UART0", "pinmux-u300", "uart0", "uart0"), + PIN_MAP("MMCSD", "pinmux-u300", "mmc0", "mmci"), + PIN_MAP("SPI", "pinmux-u300", "spi0", "pl022"), + PIN_MAP("UART0", "pinmux-u300", "uart0", "uart0"), }; struct u300_mux_hog { const char *name; struct device *dev; - struct pinmux *pmx; + struct pinctrl *p; }; static struct u300_mux_hog u300_mux_hogs[] = { @@ -1637,31 +1637,31 @@ static struct u300_mux_hog u300_mux_hogs[] = { }, }; -static int __init u300_pinmux_fetch(void) +static int __init u300_pinctrl_fetch(void) { int i; for (i = 0; i < ARRAY_SIZE(u300_mux_hogs); i++) { - struct pinmux *pmx; + struct pinctrl *p; int ret; - pmx = pinmux_get(u300_mux_hogs[i].dev, NULL); - if (IS_ERR(pmx)) { + p = pinctrl_get(u300_mux_hogs[i].dev, NULL); + if (IS_ERR(p)) { pr_err("u300: could not get pinmux hog %s\n", u300_mux_hogs[i].name); continue; } - ret = pinmux_enable(pmx); + ret = pinctrl_enable(p); if (ret) { pr_err("u300: could enable pinmux hog %s\n", u300_mux_hogs[i].name); continue; } - u300_mux_hogs[i].pmx = pmx; + u300_mux_hogs[i].p = p; } return 0; } -subsys_initcall(u300_pinmux_fetch); +subsys_initcall(u300_pinctrl_fetch); /* * Notice that AMBA devices are initialized before platform devices. @@ -1861,8 +1861,8 @@ void __init u300_init_devices(void) u300_assign_physmem(); /* Initialize pinmuxing */ - pinmux_register_mappings(u300_pinmux_map, - ARRAY_SIZE(u300_pinmux_map)); + pinctrl_register_mappings(u300_pinmux_map, + ARRAY_SIZE(u300_pinmux_map)); /* Register subdevices on the I2C buses */ u300_i2c_register_board_devices(); diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c index 4f10476cc1f2..75c6a6bb6c0a 100644 --- a/drivers/pinctrl/core.c +++ b/drivers/pinctrl/core.c @@ -624,7 +624,7 @@ struct pinctrl_dev *pinctrl_register(struct pinctrl_desc *pctldesc, mutex_lock(&pinctrldev_list_mutex); list_add(&pctldev->node, &pinctrldev_list); mutex_unlock(&pinctrldev_list_mutex); - pinmux_hog_maps(pctldev); + pinctrl_hog_maps(pctldev); return pctldev; out_err: @@ -645,7 +645,7 @@ void pinctrl_unregister(struct pinctrl_dev *pctldev) return; pinctrl_remove_device_debugfs(pctldev); - pinmux_unhog_maps(pctldev); + pinctrl_unhog_maps(pctldev); /* TODO: check that no pinmuxes are still active? */ mutex_lock(&pinctrldev_list_mutex); list_del(&pctldev->node); diff --git a/drivers/pinctrl/core.h b/drivers/pinctrl/core.h index 8a8b02e9c18e..7a89888fce94 100644 --- a/drivers/pinctrl/core.h +++ b/drivers/pinctrl/core.h @@ -28,8 +28,8 @@ struct pinctrl_gpio_range; * @owner: module providing the pin controller, used for refcounting * @driver_data: driver data for drivers registering to the pin controller * subsystem - * @pinmux_hogs_lock: lock for the pinmux hog list - * @pinmux_hogs: list of pinmux maps hogged by this device + * @pinctrl_hogs_lock: lock for the pin control hog list + * @pinctrl_hogs: list of pin control maps hogged by this device */ struct pinctrl_dev { struct list_head node; @@ -45,8 +45,8 @@ struct pinctrl_dev { struct dentry *device_root; #endif #ifdef CONFIG_PINMUX - struct mutex pinmux_hogs_lock; - struct list_head pinmux_hogs; + struct mutex pinctrl_hogs_lock; + struct list_head pinctrl_hogs; #endif }; diff --git a/drivers/pinctrl/pinctrl-coh901.c b/drivers/pinctrl/pinctrl-coh901.c index 8bac3f093d58..eba232a46a82 100644 --- a/drivers/pinctrl/pinctrl-coh901.c +++ b/drivers/pinctrl/pinctrl-coh901.c @@ -360,14 +360,14 @@ static int u300_gpio_request(struct gpio_chip *chip, unsigned offset) */ int gpio = chip->base + offset; - return pinmux_request_gpio(gpio); + return pinctrl_request_gpio(gpio); } static void u300_gpio_free(struct gpio_chip *chip, unsigned offset) { int gpio = chip->base + offset; - pinmux_free_gpio(gpio); + pinctrl_free_gpio(gpio); } static int u300_gpio_get(struct gpio_chip *chip, unsigned offset) diff --git a/drivers/pinctrl/pinmux.c b/drivers/pinctrl/pinmux.c index 1311f1d22002..773835d18f55 100644 --- a/drivers/pinctrl/pinmux.c +++ b/drivers/pinctrl/pinmux.c @@ -1,7 +1,7 @@ /* * Core driver for the pin muxing portions of the pin control subsystem * - * Copyright (C) 2011 ST-Ericsson SA + * Copyright (C) 2011-2012 ST-Ericsson SA * Written on behalf of Linaro for ST-Ericsson * Based on bits of regulator core, gpio core and clk core * @@ -29,13 +29,13 @@ #include #include "core.h" -/* List of pinmuxes */ -static DEFINE_MUTEX(pinmux_list_mutex); -static LIST_HEAD(pinmux_list); +/* List of pin controller handles */ +static DEFINE_MUTEX(pinctrl_list_mutex); +static LIST_HEAD(pinctrl_list); -/* Global pinmux maps */ -static struct pinmux_map *pinmux_maps; -static unsigned pinmux_maps_num; +/* Global pinctrl maps */ +static struct pinctrl_map *pinctrl_maps; +static unsigned pinctrl_maps_num; /** * struct pinmux_group - group list item for pinmux groups @@ -48,12 +48,12 @@ struct pinmux_group { }; /** - * struct pinmux - per-device pinmux state holder + * struct pinctrl - per-device pin control state holder * @node: global list node - * @dev: the device using this pinmux - * @usecount: the number of active users of this mux setting, used to keep - * track of nested use cases - * @pctldev: pin control device handling this pinmux + * @dev: the device using this pin control handle + * @usecount: the number of active users of this pin controller setting, used + * to keep track of nested use cases + * @pctldev: pin control device handling this pin control handle * @func_selector: the function selector for the pinmux device handling * this pinmux * @groups: the group selectors for the pinmux device and @@ -62,7 +62,7 @@ struct pinmux_group { * get/put/enable/disable * @mutex: a lock for the pinmux state holder */ -struct pinmux { +struct pinctrl { struct list_head node; struct device *dev; unsigned usecount; @@ -73,15 +73,15 @@ struct pinmux { }; /** - * struct pinmux_hog - a list item to stash mux hogs - * @node: pinmux hog list node + * struct pinctrl_hog - a list item to stash control hogs + * @node: pin control hog list node * @map: map entry responsible for this hogging - * @pmx: the pinmux hogged by this item + * @pmx: the pin control hogged by this item */ -struct pinmux_hog { +struct pinctrl_hog { struct list_head node; - struct pinmux_map const *map; - struct pinmux *pmx; + struct pinctrl_map const *map; + struct pinctrl *p; }; /** @@ -207,14 +207,14 @@ static const char *pin_free(struct pinctrl_dev *pctldev, int pin, } /** - * pinmux_request_gpio() - request a single pin to be muxed in as GPIO + * pinctrl_request_gpio() - request a single pin to be used in as GPIO * @gpio: the GPIO pin number from the GPIO subsystem number space * * This function should *ONLY* be used from gpiolib-based GPIO drivers, * as part of their gpio_request() semantics, platforms and individual drivers * shall *NOT* request GPIO pins to be muxed in. */ -int pinmux_request_gpio(unsigned gpio) +int pinctrl_request_gpio(unsigned gpio) { char gpiostr[16]; const char *function; @@ -243,17 +243,17 @@ int pinmux_request_gpio(unsigned gpio) return ret; } -EXPORT_SYMBOL_GPL(pinmux_request_gpio); +EXPORT_SYMBOL_GPL(pinctrl_request_gpio); /** - * pinmux_free_gpio() - free a single pin, currently used as GPIO + * pinctrl_free_gpio() - free control on a single pin, currently used as GPIO * @gpio: the GPIO pin number from the GPIO subsystem number space * * This function should *ONLY* be used from gpiolib-based GPIO drivers, * as part of their gpio_free() semantics, platforms and individual drivers * shall *NOT* request GPIO pins to be muxed out. */ -void pinmux_free_gpio(unsigned gpio) +void pinctrl_free_gpio(unsigned gpio) { struct pinctrl_dev *pctldev; struct pinctrl_gpio_range *range; @@ -271,9 +271,9 @@ void pinmux_free_gpio(unsigned gpio) func = pin_free(pctldev, pin, range); kfree(func); } -EXPORT_SYMBOL_GPL(pinmux_free_gpio); +EXPORT_SYMBOL_GPL(pinctrl_free_gpio); -static int pinmux_gpio_direction(unsigned gpio, bool input) +static int pinctrl_gpio_direction(unsigned gpio, bool input) { struct pinctrl_dev *pctldev; struct pinctrl_gpio_range *range; @@ -299,36 +299,36 @@ static int pinmux_gpio_direction(unsigned gpio, bool input) } /** - * pinmux_gpio_direction_input() - request a GPIO pin to go into input mode + * pinctrl_gpio_direction_input() - request a GPIO pin to go into input mode * @gpio: the GPIO pin number from the GPIO subsystem number space * * This function should *ONLY* be used from gpiolib-based GPIO drivers, * as part of their gpio_direction_input() semantics, platforms and individual - * drivers shall *NOT* touch pinmux GPIO calls. + * drivers shall *NOT* touch pin control GPIO calls. */ -int pinmux_gpio_direction_input(unsigned gpio) +int pinctrl_gpio_direction_input(unsigned gpio) { - return pinmux_gpio_direction(gpio, true); + return pinctrl_gpio_direction(gpio, true); } -EXPORT_SYMBOL_GPL(pinmux_gpio_direction_input); +EXPORT_SYMBOL_GPL(pinctrl_gpio_direction_input); /** - * pinmux_gpio_direction_output() - request a GPIO pin to go into output mode + * pinctrl_gpio_direction_output() - request a GPIO pin to go into output mode * @gpio: the GPIO pin number from the GPIO subsystem number space * * This function should *ONLY* be used from gpiolib-based GPIO drivers, * as part of their gpio_direction_output() semantics, platforms and individual - * drivers shall *NOT* touch pinmux GPIO calls. + * drivers shall *NOT* touch pin control GPIO calls. */ -int pinmux_gpio_direction_output(unsigned gpio) +int pinctrl_gpio_direction_output(unsigned gpio) { - return pinmux_gpio_direction(gpio, false); + return pinctrl_gpio_direction(gpio, false); } -EXPORT_SYMBOL_GPL(pinmux_gpio_direction_output); +EXPORT_SYMBOL_GPL(pinctrl_gpio_direction_output); /** - * pinmux_register_mappings() - register a set of pinmux mappings - * @maps: the pinmux mappings table to register, this should be marked with + * pinctrl_register_mappings() - register a set of pin controller mappings + * @maps: the pincontrol mappings table to register, this should be marked with * __initdata so it can be discarded after boot, this function will * perform a shallow copy for the mapping entries. * @num_maps: the number of maps in the mapping table @@ -338,8 +338,8 @@ EXPORT_SYMBOL_GPL(pinmux_gpio_direction_output); * passed into this function will be owned by the pinmux core and cannot be * freed. */ -int __init pinmux_register_mappings(struct pinmux_map const *maps, - unsigned num_maps) +int __init pinctrl_register_mappings(struct pinctrl_map const *maps, + unsigned num_maps) { void *tmp_maps; int i; @@ -380,26 +380,27 @@ int __init pinmux_register_mappings(struct pinmux_map const *maps, * Make a copy of the map array - string pointers will end up in the * kernel const section anyway so these do not need to be deep copied. */ - if (!pinmux_maps_num) { + if (!pinctrl_maps_num) { /* On first call, just copy them */ tmp_maps = kmemdup(maps, - sizeof(struct pinmux_map) * num_maps, + sizeof(struct pinctrl_map) * num_maps, GFP_KERNEL); if (!tmp_maps) return -ENOMEM; } else { /* Subsequent calls, reallocate array to new size */ - size_t oldsize = sizeof(struct pinmux_map) * pinmux_maps_num; - size_t newsize = sizeof(struct pinmux_map) * num_maps; + size_t oldsize = sizeof(struct pinctrl_map) * pinctrl_maps_num; + size_t newsize = sizeof(struct pinctrl_map) * num_maps; - tmp_maps = krealloc(pinmux_maps, oldsize + newsize, GFP_KERNEL); + tmp_maps = krealloc(pinctrl_maps, + oldsize + newsize, GFP_KERNEL); if (!tmp_maps) return -ENOMEM; memcpy((tmp_maps + oldsize), maps, newsize); } - pinmux_maps = tmp_maps; - pinmux_maps_num += num_maps; + pinctrl_maps = tmp_maps; + pinctrl_maps_num += num_maps; return 0; } @@ -560,7 +561,7 @@ static int pinmux_check_pin_group(struct pinctrl_dev *pctldev, * negative otherwise */ static int pinmux_search_function(struct pinctrl_dev *pctldev, - struct pinmux_map const *map, + struct pinctrl_map const *map, unsigned *func_selector, unsigned *group_selector) { @@ -598,10 +599,10 @@ static int pinmux_search_function(struct pinctrl_dev *pctldev, * pinmux_enable_muxmap() - enable a map entry for a certain pinmux */ static int pinmux_enable_muxmap(struct pinctrl_dev *pctldev, - struct pinmux *pmx, + struct pinctrl *p, struct device *dev, const char *devname, - struct pinmux_map const *map) + struct pinctrl_map const *map) { unsigned func_selector; unsigned group_selector; @@ -615,14 +616,14 @@ static int pinmux_enable_muxmap(struct pinctrl_dev *pctldev, * by anyone else. */ - if (pmx->pctldev && pmx->pctldev != pctldev) { + if (p->pctldev && p->pctldev != pctldev) { dev_err(pctldev->dev, "different pin control devices given for device %s, function %s\n", devname, map->function); return -EINVAL; } - pmx->dev = dev; - pmx->pctldev = pctldev; + p->dev = dev; + p->pctldev = pctldev; /* Now go into the driver and try to match a function and group */ ret = pinmux_search_function(pctldev, map, &func_selector, @@ -635,14 +636,14 @@ static int pinmux_enable_muxmap(struct pinctrl_dev *pctldev, * we support several groups with one function but not several * functions with one or several groups in the same pinmux. */ - if (pmx->func_selector != UINT_MAX && - pmx->func_selector != func_selector) { + if (p->func_selector != UINT_MAX && + p->func_selector != func_selector) { dev_err(pctldev->dev, "dual function defines in the map for device %s\n", devname); return -EINVAL; } - pmx->func_selector = func_selector; + p->func_selector = func_selector; /* Now add this group selector, we may have many of them */ grp = kmalloc(sizeof(struct pinmux_group), GFP_KERNEL); @@ -654,38 +655,38 @@ static int pinmux_enable_muxmap(struct pinctrl_dev *pctldev, kfree(grp); return ret; } - list_add(&grp->node, &pmx->groups); + list_add(&grp->node, &p->groups); return 0; } -static void pinmux_free_groups(struct pinmux *pmx) +static void pinmux_free_groups(struct pinctrl *p) { struct list_head *node, *tmp; - list_for_each_safe(node, tmp, &pmx->groups) { + list_for_each_safe(node, tmp, &p->groups) { struct pinmux_group *grp = list_entry(node, struct pinmux_group, node); /* Release all pins taken by this group */ - release_pins(pmx->pctldev, grp->group_selector); + release_pins(p->pctldev, grp->group_selector); list_del(node); kfree(grp); } } /** - * pinmux_get() - retrieves the pinmux for a certain device - * @dev: the device to get the pinmux for - * @name: an optional specific mux mapping name or NULL, the name is only + * pinctrl_get() - retrieves the pin controller handle for a certain device + * @dev: the device to get the pin controller handle for + * @name: an optional specific control mapping name or NULL, the name is only * needed if you want to have more than one mapping per device, or if you - * need an anonymous pinmux (not tied to any specific device) + * need an anonymous pin control (not tied to any specific device) */ -struct pinmux *pinmux_get(struct device *dev, const char *name) +struct pinctrl *pinctrl_get(struct device *dev, const char *name) { - struct pinmux_map const *map = NULL; + struct pinctrl_map const *map = NULL; struct pinctrl_dev *pctldev = NULL; const char *devname = NULL; - struct pinmux *pmx; + struct pinctrl *p; bool found_map; unsigned num_maps = 0; int ret = -ENODEV; @@ -706,16 +707,16 @@ struct pinmux *pinmux_get(struct device *dev, const char *name) * mapping, this is what consumers will get when requesting * a pinmux handle with pinmux_get() */ - pmx = kzalloc(sizeof(struct pinmux), GFP_KERNEL); - if (pmx == NULL) + p = kzalloc(sizeof(struct pinctrl), GFP_KERNEL); + if (p == NULL) return ERR_PTR(-ENOMEM); - mutex_init(&pmx->mutex); - pmx->func_selector = UINT_MAX; - INIT_LIST_HEAD(&pmx->groups); + mutex_init(&p->mutex); + p->func_selector = UINT_MAX; + INIT_LIST_HEAD(&p->groups); - /* Iterate over the pinmux maps to locate the right ones */ - for (i = 0; i < pinmux_maps_num; i++) { - map = &pinmux_maps[i]; + /* Iterate over the pin control maps to locate the right ones */ + for (i = 0; i < pinctrl_maps_num; i++) { + map = &pinctrl_maps[i]; found_map = false; /* @@ -763,11 +764,11 @@ struct pinmux *pinmux_get(struct device *dev, const char *name) /* If this map is applicable, then apply it */ if (found_map) { - ret = pinmux_enable_muxmap(pctldev, pmx, dev, + ret = pinmux_enable_muxmap(pctldev, p, dev, devname, map); if (ret) { - pinmux_free_groups(pmx); - kfree(pmx); + pinmux_free_groups(p); + kfree(p); return ERR_PTR(ret); } num_maps++; @@ -780,7 +781,7 @@ struct pinmux *pinmux_get(struct device *dev, const char *name) pr_err("could not find any mux maps for device %s, ID %s\n", devname ? devname : "(anonymous)", name ? name : "(undefined)"); - kfree(pmx); + kfree(p); return ERR_PTR(-EINVAL); } @@ -790,96 +791,96 @@ struct pinmux *pinmux_get(struct device *dev, const char *name) name ? name : "(undefined)"); /* Add the pinmux to the global list */ - mutex_lock(&pinmux_list_mutex); - list_add(&pmx->node, &pinmux_list); - mutex_unlock(&pinmux_list_mutex); + mutex_lock(&pinctrl_list_mutex); + list_add(&p->node, &pinctrl_list); + mutex_unlock(&pinctrl_list_mutex); - return pmx; + return p; } -EXPORT_SYMBOL_GPL(pinmux_get); +EXPORT_SYMBOL_GPL(pinctrl_get); /** - * pinmux_put() - release a previously claimed pinmux - * @pmx: a pinmux previously claimed by pinmux_get() + * pinctrl_put() - release a previously claimed pin control handle + * @p: a pin control handle previously claimed by pinctrl_get() */ -void pinmux_put(struct pinmux *pmx) +void pinctrl_put(struct pinctrl *p) { - if (pmx == NULL) + if (p == NULL) return; - mutex_lock(&pmx->mutex); - if (pmx->usecount) - pr_warn("releasing pinmux with active users!\n"); + mutex_lock(&p->mutex); + if (p->usecount) + pr_warn("releasing pin control handle with active users!\n"); /* Free the groups and all acquired pins */ - pinmux_free_groups(pmx); - mutex_unlock(&pmx->mutex); + pinmux_free_groups(p); + mutex_unlock(&p->mutex); /* Remove from list */ - mutex_lock(&pinmux_list_mutex); - list_del(&pmx->node); - mutex_unlock(&pinmux_list_mutex); + mutex_lock(&pinctrl_list_mutex); + list_del(&p->node); + mutex_unlock(&pinctrl_list_mutex); - kfree(pmx); + kfree(p); } -EXPORT_SYMBOL_GPL(pinmux_put); +EXPORT_SYMBOL_GPL(pinctrl_put); /** - * pinmux_enable() - enable a certain pinmux setting - * @pmx: the pinmux to enable, previously claimed by pinmux_get() + * pinctrl_enable() - enable a certain pin controller setting + * @p: the pin control handle to enable, previously claimed by pinctrl_get() */ -int pinmux_enable(struct pinmux *pmx) +int pinctrl_enable(struct pinctrl *p) { int ret = 0; - if (pmx == NULL) + if (p == NULL) return -EINVAL; - mutex_lock(&pmx->mutex); - if (pmx->usecount++ == 0) { - struct pinctrl_dev *pctldev = pmx->pctldev; + mutex_lock(&p->mutex); + if (p->usecount++ == 0) { + struct pinctrl_dev *pctldev = p->pctldev; const struct pinmux_ops *ops = pctldev->desc->pmxops; struct pinmux_group *grp; - list_for_each_entry(grp, &pmx->groups, node) { - ret = ops->enable(pctldev, pmx->func_selector, + list_for_each_entry(grp, &p->groups, node) { + ret = ops->enable(pctldev, p->func_selector, grp->group_selector); if (ret) { /* * TODO: call disable() on all groups we called * enable() on to this point? */ - pmx->usecount--; + p->usecount--; break; } } } - mutex_unlock(&pmx->mutex); + mutex_unlock(&p->mutex); return ret; } -EXPORT_SYMBOL_GPL(pinmux_enable); +EXPORT_SYMBOL_GPL(pinctrl_enable); /** - * pinmux_disable() - disable a certain pinmux setting - * @pmx: the pinmux to disable, previously claimed by pinmux_get() + * pinctrl_disable() - disable a certain pin control setting + * @p: the pin control handle to disable, previously claimed by pinctrl_get() */ -void pinmux_disable(struct pinmux *pmx) +void pinctrl_disable(struct pinctrl *p) { - if (pmx == NULL) + if (p == NULL) return; - mutex_lock(&pmx->mutex); - if (--pmx->usecount == 0) { - struct pinctrl_dev *pctldev = pmx->pctldev; + mutex_lock(&p->mutex); + if (--p->usecount == 0) { + struct pinctrl_dev *pctldev = p->pctldev; const struct pinmux_ops *ops = pctldev->desc->pmxops; struct pinmux_group *grp; - list_for_each_entry(grp, &pmx->groups, node) { - ops->disable(pctldev, pmx->func_selector, + list_for_each_entry(grp, &p->groups, node) { + ops->disable(pctldev, p->func_selector, grp->group_selector); } } - mutex_unlock(&pmx->mutex); + mutex_unlock(&p->mutex); } -EXPORT_SYMBOL_GPL(pinmux_disable); +EXPORT_SYMBOL_GPL(pinctrl_disable); int pinmux_check_ops(struct pinctrl_dev *pctldev) { @@ -910,11 +911,11 @@ int pinmux_check_ops(struct pinctrl_dev *pctldev) } /* Hog a single map entry and add to the hoglist */ -static int pinmux_hog_map(struct pinctrl_dev *pctldev, - struct pinmux_map const *map) +static int pinctrl_hog_map(struct pinctrl_dev *pctldev, + struct pinctrl_map const *map) { - struct pinmux_hog *hog; - struct pinmux *pmx; + struct pinctrl_hog *hog; + struct pinctrl *p; int ret; if (map->dev_name) { @@ -929,61 +930,61 @@ static int pinmux_hog_map(struct pinctrl_dev *pctldev, return -EINVAL; } - hog = kzalloc(sizeof(struct pinmux_hog), GFP_KERNEL); + hog = kzalloc(sizeof(struct pinctrl_hog), GFP_KERNEL); if (!hog) return -ENOMEM; - pmx = pinmux_get(NULL, map->name); - if (IS_ERR(pmx)) { + p = pinctrl_get(NULL, map->name); + if (IS_ERR(p)) { kfree(hog); dev_err(pctldev->dev, - "could not get the %s pinmux mapping for hogging\n", + "could not get the %s pin control mapping for hogging\n", map->name); - return PTR_ERR(pmx); + return PTR_ERR(p); } - ret = pinmux_enable(pmx); + ret = pinctrl_enable(p); if (ret) { - pinmux_put(pmx); + pinctrl_put(p); kfree(hog); dev_err(pctldev->dev, - "could not enable the %s pinmux mapping for hogging\n", + "could not enable the %s pin control mapping for hogging\n", map->name); return ret; } hog->map = map; - hog->pmx = pmx; + hog->p = p; dev_info(pctldev->dev, "hogged map %s, function %s\n", map->name, map->function); - mutex_lock(&pctldev->pinmux_hogs_lock); - list_add(&hog->node, &pctldev->pinmux_hogs); - mutex_unlock(&pctldev->pinmux_hogs_lock); + mutex_lock(&pctldev->pinctrl_hogs_lock); + list_add(&hog->node, &pctldev->pinctrl_hogs); + mutex_unlock(&pctldev->pinctrl_hogs_lock); return 0; } /** - * pinmux_hog_maps() - hog specific map entries on controller device + * pinctrl_hog_maps() - hog specific map entries on controller device * @pctldev: the pin control device to hog entries on * * When the pin controllers are registered, there may be some specific pinmux * map entries that need to be hogged, i.e. get+enabled until the system shuts * down. */ -int pinmux_hog_maps(struct pinctrl_dev *pctldev) +int pinctrl_hog_maps(struct pinctrl_dev *pctldev) { struct device *dev = pctldev->dev; const char *devname = dev_name(dev); int ret; int i; - INIT_LIST_HEAD(&pctldev->pinmux_hogs); - mutex_init(&pctldev->pinmux_hogs_lock); + INIT_LIST_HEAD(&pctldev->pinctrl_hogs); + mutex_init(&pctldev->pinctrl_hogs_lock); - for (i = 0; i < pinmux_maps_num; i++) { - struct pinmux_map const *map = &pinmux_maps[i]; + for (i = 0; i < pinctrl_maps_num; i++) { + struct pinctrl_map const *map = &pinctrl_maps[i]; if (!map->hog_on_boot) continue; @@ -991,7 +992,7 @@ int pinmux_hog_maps(struct pinctrl_dev *pctldev) if (map->ctrl_dev_name && !strcmp(map->ctrl_dev_name, devname)) { /* OK time to hog! */ - ret = pinmux_hog_map(pctldev, map); + ret = pinctrl_hog_map(pctldev, map); if (ret) return ret; } @@ -1000,23 +1001,23 @@ int pinmux_hog_maps(struct pinctrl_dev *pctldev) } /** - * pinmux_unhog_maps() - unhog specific map entries on controller device + * pinctrl_unhog_maps() - unhog specific map entries on controller device * @pctldev: the pin control device to unhog entries on */ -void pinmux_unhog_maps(struct pinctrl_dev *pctldev) +void pinctrl_unhog_maps(struct pinctrl_dev *pctldev) { struct list_head *node, *tmp; - mutex_lock(&pctldev->pinmux_hogs_lock); - list_for_each_safe(node, tmp, &pctldev->pinmux_hogs) { - struct pinmux_hog *hog = - list_entry(node, struct pinmux_hog, node); - pinmux_disable(hog->pmx); - pinmux_put(hog->pmx); + mutex_lock(&pctldev->pinctrl_hogs_lock); + list_for_each_safe(node, tmp, &pctldev->pinctrl_hogs) { + struct pinctrl_hog *hog = + list_entry(node, struct pinctrl_hog, node); + pinctrl_disable(hog->p); + pinctrl_put(hog->p); list_del(node); kfree(hog); } - mutex_unlock(&pctldev->pinmux_hogs_lock); + mutex_unlock(&pctldev->pinctrl_hogs_lock); } #ifdef CONFIG_DEBUG_FS @@ -1085,11 +1086,11 @@ static int pinmux_pins_show(struct seq_file *s, void *what) static int pinmux_hogs_show(struct seq_file *s, void *what) { struct pinctrl_dev *pctldev = s->private; - struct pinmux_hog *hog; + struct pinctrl_hog *hog; - seq_puts(s, "Pinmux map hogs held by device\n"); + seq_puts(s, "Pin control map hogs held by device\n"); - list_for_each_entry(hog, &pctldev->pinmux_hogs, node) + list_for_each_entry(hog, &pctldev->pinctrl_hogs, node) seq_printf(s, "%s\n", hog->map->name); return 0; @@ -1097,11 +1098,11 @@ static int pinmux_hogs_show(struct seq_file *s, void *what) static int pinmux_show(struct seq_file *s, void *what) { - struct pinmux *pmx; + struct pinctrl *p; seq_puts(s, "Requested pinmuxes and their maps:\n"); - list_for_each_entry(pmx, &pinmux_list, node) { - struct pinctrl_dev *pctldev = pmx->pctldev; + list_for_each_entry(p, &pinctrl_list, node) { + struct pinctrl_dev *pctldev = p->pctldev; const struct pinmux_ops *pmxops; const struct pinctrl_ops *pctlops; struct pinmux_group *grp; @@ -1115,13 +1116,13 @@ static int pinmux_show(struct seq_file *s, void *what) pctlops = pctldev->desc->pctlops; seq_printf(s, "device: %s function: %s (%u),", - pinctrl_dev_get_name(pmx->pctldev), + pinctrl_dev_get_name(p->pctldev), pmxops->get_function_name(pctldev, - pmx->func_selector), - pmx->func_selector); + p->func_selector), + p->func_selector); seq_printf(s, " groups: ["); - list_for_each_entry(grp, &pmx->groups, node) { + list_for_each_entry(grp, &p->groups, node) { seq_printf(s, " %s (%u)", pctlops->get_group_name(pctldev, grp->group_selector), @@ -1130,21 +1131,21 @@ static int pinmux_show(struct seq_file *s, void *what) seq_printf(s, " ]"); seq_printf(s, " users: %u map-> %s\n", - pmx->usecount, - pmx->dev ? dev_name(pmx->dev) : "(system)"); + p->usecount, + p->dev ? dev_name(p->dev) : "(system)"); } return 0; } -static int pinmux_maps_show(struct seq_file *s, void *what) +static int pinctrl_maps_show(struct seq_file *s, void *what) { int i; - seq_puts(s, "Pinmux maps:\n"); + seq_puts(s, "Pinctrl maps:\n"); - for (i = 0; i < pinmux_maps_num; i++) { - struct pinmux_map const *map = &pinmux_maps[i]; + for (i = 0; i < pinctrl_maps_num; i++) { + struct pinctrl_map const *map = &pinctrl_maps[i]; seq_printf(s, "%s:\n", map->name); if (map->dev_name) @@ -1181,9 +1182,9 @@ static int pinmux_open(struct inode *inode, struct file *file) return single_open(file, pinmux_show, NULL); } -static int pinmux_maps_open(struct inode *inode, struct file *file) +static int pinctrl_maps_open(struct inode *inode, struct file *file) { - return single_open(file, pinmux_maps_show, NULL); + return single_open(file, pinctrl_maps_show, NULL); } static const struct file_operations pinmux_functions_ops = { @@ -1214,8 +1215,8 @@ static const struct file_operations pinmux_ops = { .release = single_release, }; -static const struct file_operations pinmux_maps_ops = { - .open = pinmux_maps_open, +static const struct file_operations pinctrl_maps_ops = { + .open = pinctrl_maps_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, @@ -1236,8 +1237,8 @@ void pinmux_init_debugfs(struct dentry *subsys_root) { debugfs_create_file("pinmuxes", S_IFREG | S_IRUGO, subsys_root, NULL, &pinmux_ops); - debugfs_create_file("pinmux-maps", S_IFREG | S_IRUGO, - subsys_root, NULL, &pinmux_maps_ops); + debugfs_create_file("pinctrl-maps", S_IFREG | S_IRUGO, + subsys_root, NULL, &pinctrl_maps_ops); } #endif /* CONFIG_DEBUG_FS */ diff --git a/drivers/pinctrl/pinmux.h b/drivers/pinctrl/pinmux.h index 97f52223fbc2..dfe81726965c 100644 --- a/drivers/pinctrl/pinmux.h +++ b/drivers/pinctrl/pinmux.h @@ -16,8 +16,8 @@ int pinmux_check_ops(struct pinctrl_dev *pctldev); void pinmux_init_device_debugfs(struct dentry *devroot, struct pinctrl_dev *pctldev); void pinmux_init_debugfs(struct dentry *subsys_root); -int pinmux_hog_maps(struct pinctrl_dev *pctldev); -void pinmux_unhog_maps(struct pinctrl_dev *pctldev); +int pinctrl_hog_maps(struct pinctrl_dev *pctldev); +void pinctrl_unhog_maps(struct pinctrl_dev *pctldev); #else @@ -35,12 +35,12 @@ static inline void pinmux_init_debugfs(struct dentry *subsys_root) { } -static inline int pinmux_hog_maps(struct pinctrl_dev *pctldev) +static inline int pinctrl_hog_maps(struct pinctrl_dev *pctldev) { return 0; } -static inline void pinmux_unhog_maps(struct pinctrl_dev *pctldev) +static inline void pinctrl_unhog_maps(struct pinctrl_dev *pctldev) { } diff --git a/include/linux/pinctrl/consumer.h b/include/linux/pinctrl/consumer.h index 9c8513d5d0fb..c7d061776293 100644 --- a/include/linux/pinctrl/consumer.h +++ b/include/linux/pinctrl/consumer.h @@ -17,56 +17,56 @@ #include "pinctrl.h" /* This struct is private to the core and should be regarded as a cookie */ -struct pinmux; +struct pinctrl; #ifdef CONFIG_PINMUX /* External interface to pinmux */ -extern int pinmux_request_gpio(unsigned gpio); -extern void pinmux_free_gpio(unsigned gpio); -extern int pinmux_gpio_direction_input(unsigned gpio); -extern int pinmux_gpio_direction_output(unsigned gpio); -extern struct pinmux * __must_check pinmux_get(struct device *dev, const char *name); -extern void pinmux_put(struct pinmux *pmx); -extern int pinmux_enable(struct pinmux *pmx); -extern void pinmux_disable(struct pinmux *pmx); +extern int pinctrl_request_gpio(unsigned gpio); +extern void pinctrl_free_gpio(unsigned gpio); +extern int pinctrl_gpio_direction_input(unsigned gpio); +extern int pinctrl_gpio_direction_output(unsigned gpio); +extern struct pinctrl * __must_check pinctrl_get(struct device *dev, const char *name); +extern void pinctrl_put(struct pinctrl *p); +extern int pinctrl_enable(struct pinctrl *p); +extern void pinctrl_disable(struct pinctrl *p); #else /* !CONFIG_PINMUX */ -static inline int pinmux_request_gpio(unsigned gpio) +static inline int pinctrl_request_gpio(unsigned gpio) { return 0; } -static inline void pinmux_free_gpio(unsigned gpio) +static inline void pinctrl_free_gpio(unsigned gpio) { } -static inline int pinmux_gpio_direction_input(unsigned gpio) +static inline int pinctrl_gpio_direction_input(unsigned gpio) { return 0; } -static inline int pinmux_gpio_direction_output(unsigned gpio) +static inline int pinctrl_gpio_direction_output(unsigned gpio) { return 0; } -static inline struct pinmux * __must_check pinmux_get(struct device *dev, const char *name) +static inline struct pinctrl * __must_check pinctrl_get(struct device *dev, const char *name) { return NULL; } -static inline void pinmux_put(struct pinmux *pmx) +static inline void pinctrl_put(struct pinctrl *p) { } -static inline int pinmux_enable(struct pinmux *pmx) +static inline int pinctrl_enable(struct pinctrl *p) { return 0; } -static inline void pinmux_disable(struct pinmux *pmx) +static inline void pinctrl_disable(struct pinctrl *p) { } diff --git a/include/linux/pinctrl/machine.h b/include/linux/pinctrl/machine.h index f8593fdc6466..a2ab524a0106 100644 --- a/include/linux/pinctrl/machine.h +++ b/include/linux/pinctrl/machine.h @@ -9,11 +9,11 @@ * * License terms: GNU General Public License (GPL) version 2 */ -#ifndef __LINUX_PINMUX_MACHINE_H -#define __LINUX_PINMUX_MACHINE_H +#ifndef __LINUX_PINCTRL_MACHINE_H +#define __LINUX_PINCTRL_MACHINE_H /** - * struct pinmux_map - boards/machines shall provide this map for devices + * struct pinctrl_map - boards/machines shall provide this map for devices * @name: the name of this specific map entry for the particular machine. * This is the second parameter passed to pinmux_get() when you want * to have several mappings to the same device @@ -34,7 +34,7 @@ * a pinmux device supporting it is registered. These maps will not be * disabled and put until the system shuts down. */ -struct pinmux_map { +struct pinctrl_map { const char *name; const char *ctrl_dev_name; const char *function; @@ -47,41 +47,41 @@ struct pinmux_map { * Convenience macro to set a simple map from a certain pin controller and a * certain function to a named device */ -#define PINMUX_MAP(a, b, c, d) \ +#define PIN_MAP(a, b, c, d) \ { .name = a, .ctrl_dev_name = b, .function = c, .dev_name = d } /* * Convenience macro to map a system function onto a certain pinctrl device. * System functions are not assigned to a particular device. */ -#define PINMUX_MAP_SYS(a, b, c) \ +#define PIN_MAP_SYS(a, b, c) \ { .name = a, .ctrl_dev_name = b, .function = c } /* * Convenience macro to map a system function onto a certain pinctrl device, - * to be hogged by the pinmux core until the system shuts down. + * to be hogged by the pin control core until the system shuts down. */ -#define PINMUX_MAP_SYS_HOG(a, b, c) \ +#define PIN_MAP_SYS_HOG(a, b, c) \ { .name = a, .ctrl_dev_name = b, .function = c, \ .hog_on_boot = true } /* * Convenience macro to map a system function onto a certain pinctrl device - * using a specified group, to be hogged by the pinmux core until the system - * shuts down. + * using a specified group, to be hogged by the pin control core until the + * system shuts down. */ -#define PINMUX_MAP_SYS_HOG_GROUP(a, b, c, d) \ +#define PIN_MAP_SYS_HOG_GROUP(a, b, c, d) \ { .name = a, .ctrl_dev_name = b, .function = c, .group = d, \ .hog_on_boot = true } #ifdef CONFIG_PINMUX -extern int pinmux_register_mappings(struct pinmux_map const *map, +extern int pinctrl_register_mappings(struct pinctrl_map const *map, unsigned num_maps); #else -static inline int pinmux_register_mappings(struct pinmux_map const *map, +static inline int pinctrl_register_mappings(struct pinctrl_map const *map, unsigned num_maps) { return 0; -- cgit v1.2.3 From befe5bdfbb698b3bc57c58d0bd7ca3391c9275ed Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 9 Feb 2012 19:47:48 +0100 Subject: pinctrl: factor pin control handles over to the core This moves the per-devices struct pinctrl handles and device map over from the pinmux part of the subsystem to the core pinctrl part. This makes the device handles core infrastructure with the goal of using these handles also for pin configuration, so that device drivers (or boards etc) will need one and only one handle to the pin control core. Acked-by: Stephen Warren Signed-off-by: Linus Walleij --- drivers/pinctrl/core.c | 598 +++++++++++++++++++++++++++++++- drivers/pinctrl/core.h | 30 +- drivers/pinctrl/pinmux.c | 711 +++++---------------------------------- drivers/pinctrl/pinmux.h | 67 +++- include/linux/pinctrl/consumer.h | 8 +- 5 files changed, 779 insertions(+), 635 deletions(-) (limited to 'include/linux') diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c index 75c6a6bb6c0a..ec32c545f07f 100644 --- a/drivers/pinctrl/core.c +++ b/drivers/pinctrl/core.c @@ -1,7 +1,7 @@ /* * Core driver for the pin control subsystem * - * Copyright (C) 2011 ST-Ericsson SA + * Copyright (C) 2011-2012 ST-Ericsson SA * Written on behalf of Linaro for ST-Ericsson * Based on bits of regulator core, gpio core and clk core * @@ -30,10 +30,30 @@ #include "pinmux.h" #include "pinconf.h" +/** + * struct pinctrl_hog - a list item to stash control hogs + * @node: pin control hog list node + * @map: map entry responsible for this hogging + * @pmx: the pin control hogged by this item + */ +struct pinctrl_hog { + struct list_head node; + struct pinctrl_map const *map; + struct pinctrl *p; +}; + /* Global list of pin control devices */ static DEFINE_MUTEX(pinctrldev_list_mutex); static LIST_HEAD(pinctrldev_list); +/* List of pin controller handles */ +static DEFINE_MUTEX(pinctrl_list_mutex); +static LIST_HEAD(pinctrl_list); + +/* Global pinctrl maps */ +static struct pinctrl_map *pinctrl_maps; +static unsigned pinctrl_maps_num; + const char *pinctrl_dev_get_name(struct pinctrl_dev *pctldev) { /* We're not allowed to register devices without name */ @@ -337,6 +357,476 @@ int pinctrl_get_group_selector(struct pinctrl_dev *pctldev, return -EINVAL; } +/** + * pinctrl_request_gpio() - request a single pin to be used in as GPIO + * @gpio: the GPIO pin number from the GPIO subsystem number space + * + * This function should *ONLY* be used from gpiolib-based GPIO drivers, + * as part of their gpio_request() semantics, platforms and individual drivers + * shall *NOT* request GPIO pins to be muxed in. + */ +int pinctrl_request_gpio(unsigned gpio) +{ + struct pinctrl_dev *pctldev; + struct pinctrl_gpio_range *range; + int ret; + int pin; + + ret = pinctrl_get_device_gpio_range(gpio, &pctldev, &range); + if (ret) + return -EINVAL; + + /* Convert to the pin controllers number space */ + pin = gpio - range->base + range->pin_base; + + return pinmux_request_gpio(pctldev, range, pin, gpio); +} +EXPORT_SYMBOL_GPL(pinctrl_request_gpio); + +/** + * pinctrl_free_gpio() - free control on a single pin, currently used as GPIO + * @gpio: the GPIO pin number from the GPIO subsystem number space + * + * This function should *ONLY* be used from gpiolib-based GPIO drivers, + * as part of their gpio_free() semantics, platforms and individual drivers + * shall *NOT* request GPIO pins to be muxed out. + */ +void pinctrl_free_gpio(unsigned gpio) +{ + struct pinctrl_dev *pctldev; + struct pinctrl_gpio_range *range; + int ret; + int pin; + + ret = pinctrl_get_device_gpio_range(gpio, &pctldev, &range); + if (ret) + return; + + /* Convert to the pin controllers number space */ + pin = gpio - range->base + range->pin_base; + + return pinmux_free_gpio(pctldev, pin, range); +} +EXPORT_SYMBOL_GPL(pinctrl_free_gpio); + +static int pinctrl_gpio_direction(unsigned gpio, bool input) +{ + struct pinctrl_dev *pctldev; + struct pinctrl_gpio_range *range; + int ret; + int pin; + + ret = pinctrl_get_device_gpio_range(gpio, &pctldev, &range); + if (ret) + return ret; + + /* Convert to the pin controllers number space */ + pin = gpio - range->base + range->pin_base; + + return pinmux_gpio_direction(pctldev, range, pin, input); +} + +/** + * pinctrl_gpio_direction_input() - request a GPIO pin to go into input mode + * @gpio: the GPIO pin number from the GPIO subsystem number space + * + * This function should *ONLY* be used from gpiolib-based GPIO drivers, + * as part of their gpio_direction_input() semantics, platforms and individual + * drivers shall *NOT* touch pin control GPIO calls. + */ +int pinctrl_gpio_direction_input(unsigned gpio) +{ + return pinctrl_gpio_direction(gpio, true); +} +EXPORT_SYMBOL_GPL(pinctrl_gpio_direction_input); + +/** + * pinctrl_gpio_direction_output() - request a GPIO pin to go into output mode + * @gpio: the GPIO pin number from the GPIO subsystem number space + * + * This function should *ONLY* be used from gpiolib-based GPIO drivers, + * as part of their gpio_direction_output() semantics, platforms and individual + * drivers shall *NOT* touch pin control GPIO calls. + */ +int pinctrl_gpio_direction_output(unsigned gpio) +{ + return pinctrl_gpio_direction(gpio, false); +} +EXPORT_SYMBOL_GPL(pinctrl_gpio_direction_output); + +/** + * pinctrl_get() - retrieves the pin controller handle for a certain device + * @dev: the device to get the pin controller handle for + * @name: an optional specific control mapping name or NULL, the name is only + * needed if you want to have more than one mapping per device, or if you + * need an anonymous pin control (not tied to any specific device) + */ +struct pinctrl *pinctrl_get(struct device *dev, const char *name) +{ + struct pinctrl_map const *map = NULL; + struct pinctrl_dev *pctldev = NULL; + const char *devname = NULL; + struct pinctrl *p; + bool found_map; + unsigned num_maps = 0; + int ret = -ENODEV; + int i; + + /* We must have dev or ID or both */ + if (!dev && !name) + return ERR_PTR(-EINVAL); + + if (dev) + devname = dev_name(dev); + + pr_debug("get pin control handle %s for device %s\n", name, + devname ? devname : "(none)"); + + /* + * create the state cookie holder struct pinctrl for each + * mapping, this is what consumers will get when requesting + * a pin control handle with pinctrl_get() + */ + p = kzalloc(sizeof(struct pinctrl), GFP_KERNEL); + if (p == NULL) + return ERR_PTR(-ENOMEM); + mutex_init(&p->mutex); + pinmux_init_pinctrl_handle(p); + + /* Iterate over the pin control maps to locate the right ones */ + for (i = 0; i < pinctrl_maps_num; i++) { + map = &pinctrl_maps[i]; + found_map = false; + + /* + * First, try to find the pctldev given in the map + */ + pctldev = get_pinctrl_dev_from_devname(map->ctrl_dev_name); + if (!pctldev) { + pr_warning("could not find a pinctrl device for pinmux function %s, fishy, they shall all have one\n", + map->function); + pr_warning("given pinctrl device name: %s", + map->ctrl_dev_name); + + /* Continue to check the other mappings anyway... */ + continue; + } + + pr_debug("in map, found pctldev %s to handle function %s", + dev_name(pctldev->dev), map->function); + + + /* + * If we're looking for a specific named map, this must match, + * else we loop and look for the next. + */ + if (name != NULL) { + if (map->name == NULL) + continue; + if (strcmp(map->name, name)) + continue; + } + + /* + * This is for the case where no device name is given, we + * already know that the function name matches from above + * code. + */ + if (!map->dev_name && (name != NULL)) + found_map = true; + + /* If the mapping has a device set up it must match */ + if (map->dev_name && + (!devname || !strcmp(map->dev_name, devname))) + /* MATCH! */ + found_map = true; + + /* If this map is applicable, then apply it */ + if (found_map) { + ret = pinmux_apply_muxmap(pctldev, p, dev, + devname, map); + if (ret) { + kfree(p); + return ERR_PTR(ret); + } + num_maps++; + } + } + + /* We should have atleast one map, right */ + if (!num_maps) { + pr_err("could not find any mux maps for device %s, ID %s\n", + devname ? devname : "(anonymous)", + name ? name : "(undefined)"); + kfree(p); + return ERR_PTR(-EINVAL); + } + + pr_debug("found %u mux maps for device %s, UD %s\n", + num_maps, + devname ? devname : "(anonymous)", + name ? name : "(undefined)"); + + /* Add the pinmux to the global list */ + mutex_lock(&pinctrl_list_mutex); + list_add(&p->node, &pinctrl_list); + mutex_unlock(&pinctrl_list_mutex); + + return p; +} +EXPORT_SYMBOL_GPL(pinctrl_get); + +/** + * pinctrl_put() - release a previously claimed pin control handle + * @p: a pin control handle previously claimed by pinctrl_get() + */ +void pinctrl_put(struct pinctrl *p) +{ + if (p == NULL) + return; + + mutex_lock(&p->mutex); + if (p->usecount) + pr_warn("releasing pin control handle with active users!\n"); + /* Free the groups and all acquired pins */ + pinmux_put(p); + mutex_unlock(&p->mutex); + + /* Remove from list */ + mutex_lock(&pinctrl_list_mutex); + list_del(&p->node); + mutex_unlock(&pinctrl_list_mutex); + + kfree(p); +} +EXPORT_SYMBOL_GPL(pinctrl_put); + +/** + * pinctrl_enable() - enable a certain pin controller setting + * @p: the pin control handle to enable, previously claimed by pinctrl_get() + */ +int pinctrl_enable(struct pinctrl *p) +{ + int ret = 0; + + if (p == NULL) + return -EINVAL; + mutex_lock(&p->mutex); + if (p->usecount++ == 0) { + ret = pinmux_enable(p); + if (ret) + p->usecount--; + } + mutex_unlock(&p->mutex); + return ret; +} +EXPORT_SYMBOL_GPL(pinctrl_enable); + +/** + * pinctrl_disable() - disable a certain pin control setting + * @p: the pin control handle to disable, previously claimed by pinctrl_get() + */ +void pinctrl_disable(struct pinctrl *p) +{ + if (p == NULL) + return; + + mutex_lock(&p->mutex); + if (--p->usecount == 0) { + pinmux_disable(p); + } + mutex_unlock(&p->mutex); +} +EXPORT_SYMBOL_GPL(pinctrl_disable); + +/** + * pinctrl_register_mappings() - register a set of pin controller mappings + * @maps: the pincontrol mappings table to register, this should be marked with + * __initdata so it can be discarded after boot, this function will + * perform a shallow copy for the mapping entries. + * @num_maps: the number of maps in the mapping table + * + * Only call this once during initialization of your machine, the function is + * tagged as __init and won't be callable after init has completed. The map + * passed into this function will be owned by the pinmux core and cannot be + * freed. + */ +int __init pinctrl_register_mappings(struct pinctrl_map const *maps, + unsigned num_maps) +{ + void *tmp_maps; + int i; + + pr_debug("add %d pinmux maps\n", num_maps); + + /* First sanity check the new mapping */ + for (i = 0; i < num_maps; i++) { + if (!maps[i].name) { + pr_err("failed to register map %d: no map name given\n", + i); + return -EINVAL; + } + + if (!maps[i].ctrl_dev_name) { + pr_err("failed to register map %s (%d): no pin control device given\n", + maps[i].name, i); + return -EINVAL; + } + + if (!maps[i].function) { + pr_err("failed to register map %s (%d): no function ID given\n", + maps[i].name, i); + return -EINVAL; + } + + if (!maps[i].dev_name) + pr_debug("add system map %s function %s with no device\n", + maps[i].name, + maps[i].function); + else + pr_debug("register map %s, function %s\n", + maps[i].name, + maps[i].function); + } + + /* + * Make a copy of the map array - string pointers will end up in the + * kernel const section anyway so these do not need to be deep copied. + */ + if (!pinctrl_maps_num) { + /* On first call, just copy them */ + tmp_maps = kmemdup(maps, + sizeof(struct pinctrl_map) * num_maps, + GFP_KERNEL); + if (!tmp_maps) + return -ENOMEM; + } else { + /* Subsequent calls, reallocate array to new size */ + size_t oldsize = sizeof(struct pinctrl_map) * pinctrl_maps_num; + size_t newsize = sizeof(struct pinctrl_map) * num_maps; + + tmp_maps = krealloc(pinctrl_maps, + oldsize + newsize, GFP_KERNEL); + if (!tmp_maps) + return -ENOMEM; + memcpy((tmp_maps + oldsize), maps, newsize); + } + + pinctrl_maps = tmp_maps; + pinctrl_maps_num += num_maps; + return 0; +} + +/* Hog a single map entry and add to the hoglist */ +static int pinctrl_hog_map(struct pinctrl_dev *pctldev, + struct pinctrl_map const *map) +{ + struct pinctrl_hog *hog; + struct pinctrl *p; + int ret; + + if (map->dev_name) { + /* + * TODO: the day we have device tree support, we can + * traverse the device tree and hog to specific device nodes + * without any problems, so then we can hog pinmuxes for + * all devices that just want a static pin mux at this point. + */ + dev_err(pctldev->dev, "map %s wants to hog a non-system pinmux, this is not going to work\n", + map->name); + return -EINVAL; + } + + hog = kzalloc(sizeof(struct pinctrl_hog), GFP_KERNEL); + if (!hog) + return -ENOMEM; + + p = pinctrl_get(NULL, map->name); + if (IS_ERR(p)) { + kfree(hog); + dev_err(pctldev->dev, + "could not get the %s pin control mapping for hogging\n", + map->name); + return PTR_ERR(p); + } + + ret = pinctrl_enable(p); + if (ret) { + pinctrl_put(p); + kfree(hog); + dev_err(pctldev->dev, + "could not enable the %s pin control mapping for hogging\n", + map->name); + return ret; + } + + hog->map = map; + hog->p = p; + + dev_info(pctldev->dev, "hogged map %s, function %s\n", map->name, + map->function); + mutex_lock(&pctldev->pinctrl_hogs_lock); + list_add(&hog->node, &pctldev->pinctrl_hogs); + mutex_unlock(&pctldev->pinctrl_hogs_lock); + + return 0; +} + +/** + * pinctrl_hog_maps() - hog specific map entries on controller device + * @pctldev: the pin control device to hog entries on + * + * When the pin controllers are registered, there may be some specific pinmux + * map entries that need to be hogged, i.e. get+enabled until the system shuts + * down. + */ +int pinctrl_hog_maps(struct pinctrl_dev *pctldev) +{ + struct device *dev = pctldev->dev; + const char *devname = dev_name(dev); + int ret; + int i; + + INIT_LIST_HEAD(&pctldev->pinctrl_hogs); + mutex_init(&pctldev->pinctrl_hogs_lock); + + for (i = 0; i < pinctrl_maps_num; i++) { + struct pinctrl_map const *map = &pinctrl_maps[i]; + + if (!map->hog_on_boot) + continue; + + if (map->ctrl_dev_name && + !strcmp(map->ctrl_dev_name, devname)) { + /* OK time to hog! */ + ret = pinctrl_hog_map(pctldev, map); + if (ret) + return ret; + } + } + return 0; +} + +/** + * pinctrl_unhog_maps() - unhog specific map entries on controller device + * @pctldev: the pin control device to unhog entries on + */ +void pinctrl_unhog_maps(struct pinctrl_dev *pctldev) +{ + struct list_head *node, *tmp; + + mutex_lock(&pctldev->pinctrl_hogs_lock); + list_for_each_safe(node, tmp, &pctldev->pinctrl_hogs) { + struct pinctrl_hog *hog = + list_entry(node, struct pinctrl_hog, node); + pinctrl_disable(hog->p); + pinctrl_put(hog->p); + list_del(node); + kfree(hog); + } + mutex_unlock(&pctldev->pinctrl_hogs_lock); +} + #ifdef CONFIG_DEBUG_FS static int pinctrl_pins_show(struct seq_file *s, void *what) @@ -427,6 +917,43 @@ static int pinctrl_gpioranges_show(struct seq_file *s, void *what) return 0; } +static int pinctrl_maps_show(struct seq_file *s, void *what) +{ + int i; + + seq_puts(s, "Pinctrl maps:\n"); + + for (i = 0; i < pinctrl_maps_num; i++) { + struct pinctrl_map const *map = &pinctrl_maps[i]; + + seq_printf(s, "%s:\n", map->name); + if (map->dev_name) + seq_printf(s, " device: %s\n", + map->dev_name); + else + seq_printf(s, " SYSTEM MUX\n"); + seq_printf(s, " controlling device %s\n", + map->ctrl_dev_name); + seq_printf(s, " function: %s\n", map->function); + seq_printf(s, " group: %s\n", map->group ? map->group : + "(default)"); + } + return 0; +} + +static int pinmux_hogs_show(struct seq_file *s, void *what) +{ + struct pinctrl_dev *pctldev = s->private; + struct pinctrl_hog *hog; + + seq_puts(s, "Pin control map hogs held by device\n"); + + list_for_each_entry(hog, &pctldev->pinctrl_hogs, node) + seq_printf(s, "%s\n", hog->map->name); + + return 0; +} + static int pinctrl_devices_show(struct seq_file *s, void *what) { struct pinctrl_dev *pctldev; @@ -450,6 +977,32 @@ static int pinctrl_devices_show(struct seq_file *s, void *what) return 0; } +static int pinctrl_show(struct seq_file *s, void *what) +{ + struct pinctrl *p; + + seq_puts(s, "Requested pin control handlers their pinmux maps:\n"); + list_for_each_entry(p, &pinctrl_list, node) { + struct pinctrl_dev *pctldev = p->pctldev; + + if (!pctldev) { + seq_puts(s, "NO PIN CONTROLLER DEVICE\n"); + continue; + } + + seq_printf(s, "device: %s", + pinctrl_dev_get_name(p->pctldev)); + + pinmux_dbg_show(s, p); + + seq_printf(s, " users: %u map-> %s\n", + p->usecount, + p->dev ? dev_name(p->dev) : "(system)"); + } + + return 0; +} + static int pinctrl_pins_open(struct inode *inode, struct file *file) { return single_open(file, pinctrl_pins_show, inode->i_private); @@ -465,11 +1018,26 @@ static int pinctrl_gpioranges_open(struct inode *inode, struct file *file) return single_open(file, pinctrl_gpioranges_show, inode->i_private); } +static int pinctrl_maps_open(struct inode *inode, struct file *file) +{ + return single_open(file, pinctrl_maps_show, inode->i_private); +} + +static int pinmux_hogs_open(struct inode *inode, struct file *file) +{ + return single_open(file, pinmux_hogs_show, inode->i_private); +} + static int pinctrl_devices_open(struct inode *inode, struct file *file) { return single_open(file, pinctrl_devices_show, NULL); } +static int pinctrl_open(struct inode *inode, struct file *file) +{ + return single_open(file, pinctrl_show, NULL); +} + static const struct file_operations pinctrl_pins_ops = { .open = pinctrl_pins_open, .read = seq_read, @@ -491,6 +1059,20 @@ static const struct file_operations pinctrl_gpioranges_ops = { .release = single_release, }; +static const struct file_operations pinctrl_maps_ops = { + .open = pinctrl_maps_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static const struct file_operations pinmux_hogs_ops = { + .open = pinmux_hogs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + static const struct file_operations pinctrl_devices_ops = { .open = pinctrl_devices_open, .read = seq_read, @@ -498,6 +1080,13 @@ static const struct file_operations pinctrl_devices_ops = { .release = single_release, }; +static const struct file_operations pinctrl_ops = { + .open = pinctrl_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + static struct dentry *debugfs_root; static void pinctrl_init_device_debugfs(struct pinctrl_dev *pctldev) @@ -519,6 +1108,10 @@ static void pinctrl_init_device_debugfs(struct pinctrl_dev *pctldev) device_root, pctldev, &pinctrl_groups_ops); debugfs_create_file("gpio-ranges", S_IFREG | S_IRUGO, device_root, pctldev, &pinctrl_gpioranges_ops); + debugfs_create_file("pinctrl-maps", S_IFREG | S_IRUGO, + device_root, pctldev, &pinctrl_maps_ops); + debugfs_create_file("pinmux-hogs", S_IFREG | S_IRUGO, + device_root, pctldev, &pinmux_hogs_ops); pinmux_init_device_debugfs(device_root, pctldev); pinconf_init_device_debugfs(device_root, pctldev); } @@ -539,7 +1132,8 @@ static void pinctrl_init_debugfs(void) debugfs_create_file("pinctrl-devices", S_IFREG | S_IRUGO, debugfs_root, NULL, &pinctrl_devices_ops); - pinmux_init_debugfs(debugfs_root); + debugfs_create_file("pinctrl-handles", S_IFREG | S_IRUGO, + debugfs_root, NULL, &pinctrl_ops); } #else /* CONFIG_DEBUG_FS */ diff --git a/drivers/pinctrl/core.h b/drivers/pinctrl/core.h index 7a89888fce94..a50cdb053c84 100644 --- a/drivers/pinctrl/core.h +++ b/drivers/pinctrl/core.h @@ -30,6 +30,7 @@ struct pinctrl_gpio_range; * subsystem * @pinctrl_hogs_lock: lock for the pin control hog list * @pinctrl_hogs: list of pin control maps hogged by this device + * @device_root: debugfs root for this device */ struct pinctrl_dev { struct list_head node; @@ -41,12 +42,37 @@ struct pinctrl_dev { struct device *dev; struct module *owner; void *driver_data; + struct mutex pinctrl_hogs_lock; + struct list_head pinctrl_hogs; #ifdef CONFIG_DEBUG_FS struct dentry *device_root; #endif +}; + +/** + * struct pinctrl - per-device pin control state holder + * @node: global list node + * @dev: the device using this pin control handle + * @usecount: the number of active users of this pin controller setting, used + * to keep track of nested use cases + * @pctldev: pin control device handling this pin control handle + * @mutex: a lock for the pin control state holder + * @func_selector: the function selector for the pinmux device handling + * this pinmux + * @groups: the group selectors for the pinmux device and + * selector combination handling this pinmux, this is a list that + * will be traversed on all pinmux operations such as + * get/put/enable/disable + */ +struct pinctrl { + struct list_head node; + struct device *dev; + unsigned usecount; + struct pinctrl_dev *pctldev; + struct mutex mutex; #ifdef CONFIG_PINMUX - struct mutex pinctrl_hogs_lock; - struct list_head pinctrl_hogs; + unsigned func_selector; + struct list_head groups; #endif }; diff --git a/drivers/pinctrl/pinmux.c b/drivers/pinctrl/pinmux.c index 773835d18f55..fe4a00751c60 100644 --- a/drivers/pinctrl/pinmux.c +++ b/drivers/pinctrl/pinmux.c @@ -28,14 +28,7 @@ #include #include #include "core.h" - -/* List of pin controller handles */ -static DEFINE_MUTEX(pinctrl_list_mutex); -static LIST_HEAD(pinctrl_list); - -/* Global pinctrl maps */ -static struct pinctrl_map *pinctrl_maps; -static unsigned pinctrl_maps_num; +#include "pinmux.h" /** * struct pinmux_group - group list item for pinmux groups @@ -47,43 +40,6 @@ struct pinmux_group { unsigned group_selector; }; -/** - * struct pinctrl - per-device pin control state holder - * @node: global list node - * @dev: the device using this pin control handle - * @usecount: the number of active users of this pin controller setting, used - * to keep track of nested use cases - * @pctldev: pin control device handling this pin control handle - * @func_selector: the function selector for the pinmux device handling - * this pinmux - * @groups: the group selectors for the pinmux device and - * selector combination handling this pinmux, this is a list that - * will be traversed on all pinmux operations such as - * get/put/enable/disable - * @mutex: a lock for the pinmux state holder - */ -struct pinctrl { - struct list_head node; - struct device *dev; - unsigned usecount; - struct pinctrl_dev *pctldev; - unsigned func_selector; - struct list_head groups; - struct mutex mutex; -}; - -/** - * struct pinctrl_hog - a list item to stash control hogs - * @node: pin control hog list node - * @map: map entry responsible for this hogging - * @pmx: the pin control hogged by this item - */ -struct pinctrl_hog { - struct list_head node; - struct pinctrl_map const *map; - struct pinctrl *p; -}; - /** * pin_request() - request a single pin to be muxed in, typically for GPIO * @pin: the pin number in the global pin space @@ -207,28 +163,18 @@ static const char *pin_free(struct pinctrl_dev *pctldev, int pin, } /** - * pinctrl_request_gpio() - request a single pin to be used in as GPIO - * @gpio: the GPIO pin number from the GPIO subsystem number space - * - * This function should *ONLY* be used from gpiolib-based GPIO drivers, - * as part of their gpio_request() semantics, platforms and individual drivers - * shall *NOT* request GPIO pins to be muxed in. + * pinmux_request_gpio() - request pinmuxing for a GPIO pin + * @pctldev: pin controller device affected + * @pin: the pin to mux in for GPIO + * @range: the applicable GPIO range */ -int pinctrl_request_gpio(unsigned gpio) +int pinmux_request_gpio(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned pin, unsigned gpio) { char gpiostr[16]; const char *function; - struct pinctrl_dev *pctldev; - struct pinctrl_gpio_range *range; int ret; - int pin; - - ret = pinctrl_get_device_gpio_range(gpio, &pctldev, &range); - if (ret) - return -EINVAL; - - /* Convert to the pin controllers number space */ - pin = gpio - range->base + range->pin_base; /* Conjure some name stating what chip and pin this is taken by */ snprintf(gpiostr, 15, "%s:%d", range->name, gpio); @@ -243,53 +189,38 @@ int pinctrl_request_gpio(unsigned gpio) return ret; } -EXPORT_SYMBOL_GPL(pinctrl_request_gpio); /** - * pinctrl_free_gpio() - free control on a single pin, currently used as GPIO - * @gpio: the GPIO pin number from the GPIO subsystem number space - * - * This function should *ONLY* be used from gpiolib-based GPIO drivers, - * as part of their gpio_free() semantics, platforms and individual drivers - * shall *NOT* request GPIO pins to be muxed out. + * pinmux_free_gpio() - release a pin from GPIO muxing + * @pctldev: the pin controller device for the pin + * @pin: the affected currently GPIO-muxed in pin + * @range: applicable GPIO range */ -void pinctrl_free_gpio(unsigned gpio) +void pinmux_free_gpio(struct pinctrl_dev *pctldev, unsigned pin, + struct pinctrl_gpio_range *range) { - struct pinctrl_dev *pctldev; - struct pinctrl_gpio_range *range; - int ret; - int pin; const char *func; - ret = pinctrl_get_device_gpio_range(gpio, &pctldev, &range); - if (ret) - return; - - /* Convert to the pin controllers number space */ - pin = gpio - range->base + range->pin_base; - func = pin_free(pctldev, pin, range); kfree(func); } -EXPORT_SYMBOL_GPL(pinctrl_free_gpio); -static int pinctrl_gpio_direction(unsigned gpio, bool input) +/** + * pinmux_gpio_direction() - set the direction of a single muxed-in GPIO pin + * @pctldev: the pin controller handling this pin + * @range: applicable GPIO range + * @pin: the affected GPIO pin in this controller + * @input: true if we set the pin as input, false for output + */ +int pinmux_gpio_direction(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned pin, bool input) { - struct pinctrl_dev *pctldev; - struct pinctrl_gpio_range *range; const struct pinmux_ops *ops; int ret; - int pin; - - ret = pinctrl_get_device_gpio_range(gpio, &pctldev, &range); - if (ret) - return ret; ops = pctldev->desc->pmxops; - /* Convert to the pin controllers number space */ - pin = gpio - range->base + range->pin_base; - if (ops->gpio_set_direction) ret = ops->gpio_set_direction(pctldev, range, pin, input); else @@ -298,112 +229,6 @@ static int pinctrl_gpio_direction(unsigned gpio, bool input) return ret; } -/** - * pinctrl_gpio_direction_input() - request a GPIO pin to go into input mode - * @gpio: the GPIO pin number from the GPIO subsystem number space - * - * This function should *ONLY* be used from gpiolib-based GPIO drivers, - * as part of their gpio_direction_input() semantics, platforms and individual - * drivers shall *NOT* touch pin control GPIO calls. - */ -int pinctrl_gpio_direction_input(unsigned gpio) -{ - return pinctrl_gpio_direction(gpio, true); -} -EXPORT_SYMBOL_GPL(pinctrl_gpio_direction_input); - -/** - * pinctrl_gpio_direction_output() - request a GPIO pin to go into output mode - * @gpio: the GPIO pin number from the GPIO subsystem number space - * - * This function should *ONLY* be used from gpiolib-based GPIO drivers, - * as part of their gpio_direction_output() semantics, platforms and individual - * drivers shall *NOT* touch pin control GPIO calls. - */ -int pinctrl_gpio_direction_output(unsigned gpio) -{ - return pinctrl_gpio_direction(gpio, false); -} -EXPORT_SYMBOL_GPL(pinctrl_gpio_direction_output); - -/** - * pinctrl_register_mappings() - register a set of pin controller mappings - * @maps: the pincontrol mappings table to register, this should be marked with - * __initdata so it can be discarded after boot, this function will - * perform a shallow copy for the mapping entries. - * @num_maps: the number of maps in the mapping table - * - * Only call this once during initialization of your machine, the function is - * tagged as __init and won't be callable after init has completed. The map - * passed into this function will be owned by the pinmux core and cannot be - * freed. - */ -int __init pinctrl_register_mappings(struct pinctrl_map const *maps, - unsigned num_maps) -{ - void *tmp_maps; - int i; - - pr_debug("add %d pinmux maps\n", num_maps); - - /* First sanity check the new mapping */ - for (i = 0; i < num_maps; i++) { - if (!maps[i].name) { - pr_err("failed to register map %d: no map name given\n", - i); - return -EINVAL; - } - - if (!maps[i].ctrl_dev_name) { - pr_err("failed to register map %s (%d): no pin control device given\n", - maps[i].name, i); - return -EINVAL; - } - - if (!maps[i].function) { - pr_err("failed to register map %s (%d): no function ID given\n", - maps[i].name, i); - return -EINVAL; - } - - if (!maps[i].dev_name) - pr_debug("add system map %s function %s with no device\n", - maps[i].name, - maps[i].function); - else - pr_debug("register map %s, function %s\n", - maps[i].name, - maps[i].function); - } - - /* - * Make a copy of the map array - string pointers will end up in the - * kernel const section anyway so these do not need to be deep copied. - */ - if (!pinctrl_maps_num) { - /* On first call, just copy them */ - tmp_maps = kmemdup(maps, - sizeof(struct pinctrl_map) * num_maps, - GFP_KERNEL); - if (!tmp_maps) - return -ENOMEM; - } else { - /* Subsequent calls, reallocate array to new size */ - size_t oldsize = sizeof(struct pinctrl_map) * pinctrl_maps_num; - size_t newsize = sizeof(struct pinctrl_map) * num_maps; - - tmp_maps = krealloc(pinctrl_maps, - oldsize + newsize, GFP_KERNEL); - if (!tmp_maps) - return -ENOMEM; - memcpy((tmp_maps + oldsize), maps, newsize); - } - - pinctrl_maps = tmp_maps; - pinctrl_maps_num += num_maps; - return 0; -} - /** * acquire_pins() - acquire all the pins for a certain function on a pinmux * @pctldev: the device to take the pins on @@ -660,227 +485,81 @@ static int pinmux_enable_muxmap(struct pinctrl_dev *pctldev, return 0; } -static void pinmux_free_groups(struct pinctrl *p) -{ - struct list_head *node, *tmp; - - list_for_each_safe(node, tmp, &p->groups) { - struct pinmux_group *grp = - list_entry(node, struct pinmux_group, node); - /* Release all pins taken by this group */ - release_pins(p->pctldev, grp->group_selector); - list_del(node); - kfree(grp); - } -} - /** - * pinctrl_get() - retrieves the pin controller handle for a certain device - * @dev: the device to get the pin controller handle for - * @name: an optional specific control mapping name or NULL, the name is only - * needed if you want to have more than one mapping per device, or if you - * need an anonymous pin control (not tied to any specific device) + * pinmux_apply_muxmap() - apply a certain mux mapping entry */ -struct pinctrl *pinctrl_get(struct device *dev, const char *name) +int pinmux_apply_muxmap(struct pinctrl_dev *pctldev, + struct pinctrl *p, + struct device *dev, + const char *devname, + struct pinctrl_map const *map) { - struct pinctrl_map const *map = NULL; - struct pinctrl_dev *pctldev = NULL; - const char *devname = NULL; - struct pinctrl *p; - bool found_map; - unsigned num_maps = 0; - int ret = -ENODEV; - int i; - - /* We must have dev or ID or both */ - if (!dev && !name) - return ERR_PTR(-EINVAL); - - if (dev) - devname = dev_name(dev); - - pr_debug("get mux %s for device %s\n", name, - devname ? devname : "(none)"); - - /* - * create the state cookie holder struct pinmux for each - * mapping, this is what consumers will get when requesting - * a pinmux handle with pinmux_get() - */ - p = kzalloc(sizeof(struct pinctrl), GFP_KERNEL); - if (p == NULL) - return ERR_PTR(-ENOMEM); - mutex_init(&p->mutex); - p->func_selector = UINT_MAX; - INIT_LIST_HEAD(&p->groups); - - /* Iterate over the pin control maps to locate the right ones */ - for (i = 0; i < pinctrl_maps_num; i++) { - map = &pinctrl_maps[i]; - found_map = false; - - /* - * First, try to find the pctldev given in the map - */ - pctldev = get_pinctrl_dev_from_devname(map->ctrl_dev_name); - if (!pctldev) { - pr_warning("could not find a pinctrl device for pinmux function %s, fishy, they shall all have one\n", - map->function); - pr_warning("given pinctrl device name: %s", - map->ctrl_dev_name); - - /* Continue to check the other mappings anyway... */ - continue; - } - - pr_debug("in map, found pctldev %s to handle function %s", - dev_name(pctldev->dev), map->function); - - - /* - * If we're looking for a specific named map, this must match, - * else we loop and look for the next. - */ - if (name != NULL) { - if (map->name == NULL) - continue; - if (strcmp(map->name, name)) - continue; - } - - /* - * This is for the case where no device name is given, we - * already know that the function name matches from above - * code. - */ - if (!map->dev_name && (name != NULL)) - found_map = true; - - /* If the mapping has a device set up it must match */ - if (map->dev_name && - (!devname || !strcmp(map->dev_name, devname))) - /* MATCH! */ - found_map = true; - - /* If this map is applicable, then apply it */ - if (found_map) { - ret = pinmux_enable_muxmap(pctldev, p, dev, - devname, map); - if (ret) { - pinmux_free_groups(p); - kfree(p); - return ERR_PTR(ret); - } - num_maps++; - } - } - + int ret; - /* We should have atleast one map, right */ - if (!num_maps) { - pr_err("could not find any mux maps for device %s, ID %s\n", - devname ? devname : "(anonymous)", - name ? name : "(undefined)"); - kfree(p); - return ERR_PTR(-EINVAL); + ret = pinmux_enable_muxmap(pctldev, p, dev, + devname, map); + if (ret) { + pinmux_put(p); + return ret; } - pr_debug("found %u mux maps for device %s, UD %s\n", - num_maps, - devname ? devname : "(anonymous)", - name ? name : "(undefined)"); - - /* Add the pinmux to the global list */ - mutex_lock(&pinctrl_list_mutex); - list_add(&p->node, &pinctrl_list); - mutex_unlock(&pinctrl_list_mutex); - - return p; + return 0; } -EXPORT_SYMBOL_GPL(pinctrl_get); /** - * pinctrl_put() - release a previously claimed pin control handle - * @p: a pin control handle previously claimed by pinctrl_get() + * pinmux_put() - free up the pinmux portions of a pin controller handle */ -void pinctrl_put(struct pinctrl *p) +void pinmux_put(struct pinctrl *p) { - if (p == NULL) - return; - - mutex_lock(&p->mutex); - if (p->usecount) - pr_warn("releasing pin control handle with active users!\n"); - /* Free the groups and all acquired pins */ - pinmux_free_groups(p); - mutex_unlock(&p->mutex); - - /* Remove from list */ - mutex_lock(&pinctrl_list_mutex); - list_del(&p->node); - mutex_unlock(&pinctrl_list_mutex); + struct list_head *node, *tmp; - kfree(p); + list_for_each_safe(node, tmp, &p->groups) { + struct pinmux_group *grp = + list_entry(node, struct pinmux_group, node); + /* Release all pins taken by this group */ + release_pins(p->pctldev, grp->group_selector); + list_del(node); + kfree(grp); + } } -EXPORT_SYMBOL_GPL(pinctrl_put); /** - * pinctrl_enable() - enable a certain pin controller setting - * @p: the pin control handle to enable, previously claimed by pinctrl_get() + * pinmux_enable() - enable the pinmux portion of a pin control handle */ -int pinctrl_enable(struct pinctrl *p) +int pinmux_enable(struct pinctrl *p) { - int ret = 0; + struct pinctrl_dev *pctldev = p->pctldev; + const struct pinmux_ops *ops = pctldev->desc->pmxops; + struct pinmux_group *grp; + int ret; - if (p == NULL) - return -EINVAL; - mutex_lock(&p->mutex); - if (p->usecount++ == 0) { - struct pinctrl_dev *pctldev = p->pctldev; - const struct pinmux_ops *ops = pctldev->desc->pmxops; - struct pinmux_group *grp; - - list_for_each_entry(grp, &p->groups, node) { - ret = ops->enable(pctldev, p->func_selector, - grp->group_selector); - if (ret) { - /* - * TODO: call disable() on all groups we called - * enable() on to this point? - */ - p->usecount--; - break; - } - } + list_for_each_entry(grp, &p->groups, node) { + ret = ops->enable(pctldev, p->func_selector, + grp->group_selector); + if (ret) + /* + * TODO: call disable() on all groups we called + * enable() on to this point? + */ + return ret; } - mutex_unlock(&p->mutex); - return ret; + return 0; } -EXPORT_SYMBOL_GPL(pinctrl_enable); /** - * pinctrl_disable() - disable a certain pin control setting - * @p: the pin control handle to disable, previously claimed by pinctrl_get() + * pinmux_disable() - disable the pinmux portions of a pin control handle */ -void pinctrl_disable(struct pinctrl *p) +void pinmux_disable(struct pinctrl *p) { - if (p == NULL) - return; - - mutex_lock(&p->mutex); - if (--p->usecount == 0) { - struct pinctrl_dev *pctldev = p->pctldev; - const struct pinmux_ops *ops = pctldev->desc->pmxops; - struct pinmux_group *grp; + struct pinctrl_dev *pctldev = p->pctldev; + const struct pinmux_ops *ops = pctldev->desc->pmxops; + struct pinmux_group *grp; - list_for_each_entry(grp, &p->groups, node) { - ops->disable(pctldev, p->func_selector, - grp->group_selector); - } + list_for_each_entry(grp, &p->groups, node) { + ops->disable(pctldev, p->func_selector, + grp->group_selector); } - mutex_unlock(&p->mutex); } -EXPORT_SYMBOL_GPL(pinctrl_disable); int pinmux_check_ops(struct pinctrl_dev *pctldev) { @@ -910,116 +589,6 @@ int pinmux_check_ops(struct pinctrl_dev *pctldev) return 0; } -/* Hog a single map entry and add to the hoglist */ -static int pinctrl_hog_map(struct pinctrl_dev *pctldev, - struct pinctrl_map const *map) -{ - struct pinctrl_hog *hog; - struct pinctrl *p; - int ret; - - if (map->dev_name) { - /* - * TODO: the day we have device tree support, we can - * traverse the device tree and hog to specific device nodes - * without any problems, so then we can hog pinmuxes for - * all devices that just want a static pin mux at this point. - */ - dev_err(pctldev->dev, "map %s wants to hog a non-system pinmux, this is not going to work\n", - map->name); - return -EINVAL; - } - - hog = kzalloc(sizeof(struct pinctrl_hog), GFP_KERNEL); - if (!hog) - return -ENOMEM; - - p = pinctrl_get(NULL, map->name); - if (IS_ERR(p)) { - kfree(hog); - dev_err(pctldev->dev, - "could not get the %s pin control mapping for hogging\n", - map->name); - return PTR_ERR(p); - } - - ret = pinctrl_enable(p); - if (ret) { - pinctrl_put(p); - kfree(hog); - dev_err(pctldev->dev, - "could not enable the %s pin control mapping for hogging\n", - map->name); - return ret; - } - - hog->map = map; - hog->p = p; - - dev_info(pctldev->dev, "hogged map %s, function %s\n", map->name, - map->function); - mutex_lock(&pctldev->pinctrl_hogs_lock); - list_add(&hog->node, &pctldev->pinctrl_hogs); - mutex_unlock(&pctldev->pinctrl_hogs_lock); - - return 0; -} - -/** - * pinctrl_hog_maps() - hog specific map entries on controller device - * @pctldev: the pin control device to hog entries on - * - * When the pin controllers are registered, there may be some specific pinmux - * map entries that need to be hogged, i.e. get+enabled until the system shuts - * down. - */ -int pinctrl_hog_maps(struct pinctrl_dev *pctldev) -{ - struct device *dev = pctldev->dev; - const char *devname = dev_name(dev); - int ret; - int i; - - INIT_LIST_HEAD(&pctldev->pinctrl_hogs); - mutex_init(&pctldev->pinctrl_hogs_lock); - - for (i = 0; i < pinctrl_maps_num; i++) { - struct pinctrl_map const *map = &pinctrl_maps[i]; - - if (!map->hog_on_boot) - continue; - - if (map->ctrl_dev_name && - !strcmp(map->ctrl_dev_name, devname)) { - /* OK time to hog! */ - ret = pinctrl_hog_map(pctldev, map); - if (ret) - return ret; - } - } - return 0; -} - -/** - * pinctrl_unhog_maps() - unhog specific map entries on controller device - * @pctldev: the pin control device to unhog entries on - */ -void pinctrl_unhog_maps(struct pinctrl_dev *pctldev) -{ - struct list_head *node, *tmp; - - mutex_lock(&pctldev->pinctrl_hogs_lock); - list_for_each_safe(node, tmp, &pctldev->pinctrl_hogs) { - struct pinctrl_hog *hog = - list_entry(node, struct pinctrl_hog, node); - pinctrl_disable(hog->p); - pinctrl_put(hog->p); - list_del(node); - kfree(hog); - } - mutex_unlock(&pctldev->pinctrl_hogs_lock); -} - #ifdef CONFIG_DEBUG_FS /* Called from pincontrol core */ @@ -1083,83 +652,29 @@ static int pinmux_pins_show(struct seq_file *s, void *what) return 0; } -static int pinmux_hogs_show(struct seq_file *s, void *what) -{ - struct pinctrl_dev *pctldev = s->private; - struct pinctrl_hog *hog; - - seq_puts(s, "Pin control map hogs held by device\n"); - - list_for_each_entry(hog, &pctldev->pinctrl_hogs, node) - seq_printf(s, "%s\n", hog->map->name); - - return 0; -} - -static int pinmux_show(struct seq_file *s, void *what) +void pinmux_dbg_show(struct seq_file *s, struct pinctrl *p) { - struct pinctrl *p; - - seq_puts(s, "Requested pinmuxes and their maps:\n"); - list_for_each_entry(p, &pinctrl_list, node) { - struct pinctrl_dev *pctldev = p->pctldev; - const struct pinmux_ops *pmxops; - const struct pinctrl_ops *pctlops; - struct pinmux_group *grp; - - if (!pctldev) { - seq_puts(s, "NO PIN CONTROLLER DEVICE\n"); - continue; - } - - pmxops = pctldev->desc->pmxops; - pctlops = pctldev->desc->pctlops; - - seq_printf(s, "device: %s function: %s (%u),", - pinctrl_dev_get_name(p->pctldev), - pmxops->get_function_name(pctldev, - p->func_selector), - p->func_selector); - - seq_printf(s, " groups: ["); - list_for_each_entry(grp, &p->groups, node) { - seq_printf(s, " %s (%u)", - pctlops->get_group_name(pctldev, - grp->group_selector), - grp->group_selector); - } - seq_printf(s, " ]"); + struct pinctrl_dev *pctldev = p->pctldev; + const struct pinmux_ops *pmxops; + const struct pinctrl_ops *pctlops; + struct pinmux_group *grp; - seq_printf(s, " users: %u map-> %s\n", - p->usecount, - p->dev ? dev_name(p->dev) : "(system)"); - } + pmxops = pctldev->desc->pmxops; + pctlops = pctldev->desc->pctlops; - return 0; -} - -static int pinctrl_maps_show(struct seq_file *s, void *what) -{ - int i; + seq_printf(s, " function: %s (%u),", + pmxops->get_function_name(pctldev, + p->func_selector), + p->func_selector); - seq_puts(s, "Pinctrl maps:\n"); - - for (i = 0; i < pinctrl_maps_num; i++) { - struct pinctrl_map const *map = &pinctrl_maps[i]; - - seq_printf(s, "%s:\n", map->name); - if (map->dev_name) - seq_printf(s, " device: %s\n", - map->dev_name); - else - seq_printf(s, " SYSTEM MUX\n"); - seq_printf(s, " controlling device %s\n", - map->ctrl_dev_name); - seq_printf(s, " function: %s\n", map->function); - seq_printf(s, " group: %s\n", map->group ? map->group : - "(default)"); + seq_printf(s, " groups: ["); + list_for_each_entry(grp, &p->groups, node) { + seq_printf(s, " %s (%u)", + pctlops->get_group_name(pctldev, + grp->group_selector), + grp->group_selector); } - return 0; + seq_printf(s, " ]"); } static int pinmux_functions_open(struct inode *inode, struct file *file) @@ -1172,21 +687,6 @@ static int pinmux_pins_open(struct inode *inode, struct file *file) return single_open(file, pinmux_pins_show, inode->i_private); } -static int pinmux_hogs_open(struct inode *inode, struct file *file) -{ - return single_open(file, pinmux_hogs_show, inode->i_private); -} - -static int pinmux_open(struct inode *inode, struct file *file) -{ - return single_open(file, pinmux_show, NULL); -} - -static int pinctrl_maps_open(struct inode *inode, struct file *file) -{ - return single_open(file, pinctrl_maps_show, NULL); -} - static const struct file_operations pinmux_functions_ops = { .open = pinmux_functions_open, .read = seq_read, @@ -1201,27 +701,6 @@ static const struct file_operations pinmux_pins_ops = { .release = single_release, }; -static const struct file_operations pinmux_hogs_ops = { - .open = pinmux_hogs_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static const struct file_operations pinmux_ops = { - .open = pinmux_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static const struct file_operations pinctrl_maps_ops = { - .open = pinctrl_maps_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - void pinmux_init_device_debugfs(struct dentry *devroot, struct pinctrl_dev *pctldev) { @@ -1229,16 +708,6 @@ void pinmux_init_device_debugfs(struct dentry *devroot, devroot, pctldev, &pinmux_functions_ops); debugfs_create_file("pinmux-pins", S_IFREG | S_IRUGO, devroot, pctldev, &pinmux_pins_ops); - debugfs_create_file("pinmux-hogs", S_IFREG | S_IRUGO, - devroot, pctldev, &pinmux_hogs_ops); -} - -void pinmux_init_debugfs(struct dentry *subsys_root) -{ - debugfs_create_file("pinmuxes", S_IFREG | S_IRUGO, - subsys_root, NULL, &pinmux_ops); - debugfs_create_file("pinctrl-maps", S_IFREG | S_IRUGO, - subsys_root, NULL, &pinctrl_maps_ops); } #endif /* CONFIG_DEBUG_FS */ diff --git a/drivers/pinctrl/pinmux.h b/drivers/pinctrl/pinmux.h index dfe81726965c..7680a1703252 100644 --- a/drivers/pinctrl/pinmux.h +++ b/drivers/pinctrl/pinmux.h @@ -15,9 +15,28 @@ int pinmux_check_ops(struct pinctrl_dev *pctldev); void pinmux_init_device_debugfs(struct dentry *devroot, struct pinctrl_dev *pctldev); -void pinmux_init_debugfs(struct dentry *subsys_root); -int pinctrl_hog_maps(struct pinctrl_dev *pctldev); -void pinctrl_unhog_maps(struct pinctrl_dev *pctldev); +int pinmux_request_gpio(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned pin, unsigned gpio); +void pinmux_free_gpio(struct pinctrl_dev *pctldev, unsigned pin, + struct pinctrl_gpio_range *range); +int pinmux_gpio_direction(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned pin, bool input); +static inline void pinmux_init_pinctrl_handle(struct pinctrl *p) +{ + p->func_selector = UINT_MAX; + INIT_LIST_HEAD(&p->groups); +} +int pinmux_apply_muxmap(struct pinctrl_dev *pctldev, + struct pinctrl *p, + struct device *dev, + const char *devname, + struct pinctrl_map const *map); +void pinmux_put(struct pinctrl *p); +int pinmux_enable(struct pinctrl *p); +void pinmux_disable(struct pinctrl *p); +void pinmux_dbg_show(struct seq_file *s, struct pinctrl *p); #else @@ -31,16 +50,52 @@ static inline void pinmux_init_device_debugfs(struct dentry *devroot, { } -static inline void pinmux_init_debugfs(struct dentry *subsys_root) +static inline int pinmux_request_gpio(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned pin, unsigned gpio) +{ + return 0; +} + +static inline void pinmux_free_gpio(struct pinctrl_dev *pctldev, + unsigned pin, + struct pinctrl_gpio_range *range) +{ +} + +static inline int pinmux_gpio_direction(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned pin, bool input) +{ + return 0; +} + +static inline void pinmux_init_pinctrl_handle(struct pinctrl *p) { } -static inline int pinctrl_hog_maps(struct pinctrl_dev *pctldev) +static inline int pinmux_apply_muxmap(struct pinctrl_dev *pctldev, + struct pinctrl *p, + struct device *dev, + const char *devname, + struct pinctrl_map const *map) { return 0; } -static inline void pinctrl_unhog_maps(struct pinctrl_dev *pctldev) +static inline void pinmux_put(struct pinctrl *p) +{ +} + +static inline int pinmux_enable(struct pinctrl *p) +{ +} + +static inline void pinmux_disable(struct pinctrl *p) +{ +} + +void pinmux_dbg_show(struct seq_file *s, struct pinctrl *p) { } diff --git a/include/linux/pinctrl/consumer.h b/include/linux/pinctrl/consumer.h index c7d061776293..30865947f2d9 100644 --- a/include/linux/pinctrl/consumer.h +++ b/include/linux/pinctrl/consumer.h @@ -19,9 +19,9 @@ /* This struct is private to the core and should be regarded as a cookie */ struct pinctrl; -#ifdef CONFIG_PINMUX +#ifdef CONFIG_PINCTRL -/* External interface to pinmux */ +/* External interface to pin control */ extern int pinctrl_request_gpio(unsigned gpio); extern void pinctrl_free_gpio(unsigned gpio); extern int pinctrl_gpio_direction_input(unsigned gpio); @@ -31,7 +31,7 @@ extern void pinctrl_put(struct pinctrl *p); extern int pinctrl_enable(struct pinctrl *p); extern void pinctrl_disable(struct pinctrl *p); -#else /* !CONFIG_PINMUX */ +#else /* !CONFIG_PINCTRL */ static inline int pinctrl_request_gpio(unsigned gpio) { @@ -70,7 +70,7 @@ static inline void pinctrl_disable(struct pinctrl *p) { } -#endif /* CONFIG_PINMUX */ +#endif /* CONFIG_PINCTRL */ #ifdef CONFIG_PINCONF -- cgit v1.2.3 From 77a5988355f993840928d195f790a939200a4ff0 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 10 Feb 2012 01:34:12 +0100 Subject: pinctrl: changes hog mechanism to be self-referential Instead of a specific boolean field to indicate if a map entry shall be hogged, treat self-reference as an indication of desired hogging. This drops one field off the map struct and has a nice Douglas R. Hofstadter-feel to it. Acked-by: Dong Aisheng Acked-by: Stephen Warren Signed-off-by: Linus Walleij --- Documentation/pinctrl.txt | 8 ++++---- drivers/pinctrl/core.c | 6 ++---- include/linux/pinctrl/machine.h | 18 ++++++------------ 3 files changed, 12 insertions(+), 20 deletions(-) (limited to 'include/linux') diff --git a/Documentation/pinctrl.txt b/Documentation/pinctrl.txt index 2e7132355db8..ee3266b948e7 100644 --- a/Documentation/pinctrl.txt +++ b/Documentation/pinctrl.txt @@ -989,21 +989,21 @@ is registered. This means that the core will attempt to call pinctrl_get() and pinctrl_enable() on it immediately after the pin control device has been registered. -This is enabled by simply setting the .hog_on_boot field in the map to true, -like this: +This is enabled by simply setting the .dev_name field in the map to the name +of the pin controller itself, like this: { .name = "POWERMAP" .ctrl_dev_name = "pinctrl-foo", .function = "power_func", - .hog_on_boot = true, + .dev_name = "pinctrl-foo", }, Since it may be common to request the core to hog a few always-applicable mux settings on the primary pin controller, there is a convenience macro for this: -PIN_MAP_PRIMARY_SYS_HOG("POWERMAP", "power_func") +PIN_MAP_PRIMARY_SYS_HOG("POWERMAP", "pinctrl-foo", "power_func") This gives the exact same result as the above construction. diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c index ec32c545f07f..c5f76ad5a8c5 100644 --- a/drivers/pinctrl/core.c +++ b/drivers/pinctrl/core.c @@ -793,11 +793,9 @@ int pinctrl_hog_maps(struct pinctrl_dev *pctldev) for (i = 0; i < pinctrl_maps_num; i++) { struct pinctrl_map const *map = &pinctrl_maps[i]; - if (!map->hog_on_boot) - continue; - if (map->ctrl_dev_name && - !strcmp(map->ctrl_dev_name, devname)) { + !strcmp(map->ctrl_dev_name, devname) && + !strcmp(map->dev_name, devname)) { /* OK time to hog! */ ret = pinctrl_hog_map(pctldev, map); if (ret) diff --git a/include/linux/pinctrl/machine.h b/include/linux/pinctrl/machine.h index a2ab524a0106..af145d571970 100644 --- a/include/linux/pinctrl/machine.h +++ b/include/linux/pinctrl/machine.h @@ -26,13 +26,9 @@ * selects a certain specific pin group to activate for the function, if * left as NULL, the first applicable group will be used * @dev_name: the name of the device using this specific mapping, the name - * must be the same as in your struct device* - * @hog_on_boot: if this is set to true, the pin control subsystem will itself - * hog the mappings as the pinmux device drivers are attached, so this is - * typically used with system maps (mux mappings without an assigned - * device) that you want to get hogged and enabled by default as soon as - * a pinmux device supporting it is registered. These maps will not be - * disabled and put until the system shuts down. + * must be the same as in your struct device*. If this name is set to the + * same name as the pin controllers own dev_name(), the map entry will be + * hogged by the driver itself upon registration */ struct pinctrl_map { const char *name; @@ -40,7 +36,6 @@ struct pinctrl_map { const char *function; const char *group; const char *dev_name; - bool hog_on_boot; }; /* @@ -62,8 +57,7 @@ struct pinctrl_map { * to be hogged by the pin control core until the system shuts down. */ #define PIN_MAP_SYS_HOG(a, b, c) \ - { .name = a, .ctrl_dev_name = b, .function = c, \ - .hog_on_boot = true } + { .name = a, .ctrl_dev_name = b, .dev_name = b, .function = c, } /* * Convenience macro to map a system function onto a certain pinctrl device @@ -71,8 +65,8 @@ struct pinctrl_map { * system shuts down. */ #define PIN_MAP_SYS_HOG_GROUP(a, b, c, d) \ - { .name = a, .ctrl_dev_name = b, .function = c, .group = d, \ - .hog_on_boot = true } + { .name = a, .ctrl_dev_name = b, .dev_name = b, .function = c, \ + .group = d, } #ifdef CONFIG_PINMUX -- cgit v1.2.3 From a45c0ad51d344dad18c6fb67304b4f87cf0ffd59 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 24 Nov 2011 09:53:43 -0300 Subject: [media] v4l2: add VIDIOC_(TRY_)DECODER_CMD As discussed during the 2011 V4L-DVB workshop, the API in dvb/video.h should be replaced by a proper V4L2 API. This patch turns the VIDEO_(TRY_)DECODER_CMD ioctls into proper V4L2 ioctls. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/v4l2-compat-ioctl32.c | 2 ++ drivers/media/video/v4l2-ioctl.c | 28 ++++++++++++++++ include/linux/videodev2.h | 53 +++++++++++++++++++++++++++++++ include/media/v4l2-ioctl.h | 4 +++ 4 files changed, 87 insertions(+) (limited to 'include/linux') diff --git a/drivers/media/video/v4l2-compat-ioctl32.c b/drivers/media/video/v4l2-compat-ioctl32.c index af4419e6c658..e6f67aa5b4c0 100644 --- a/drivers/media/video/v4l2-compat-ioctl32.c +++ b/drivers/media/video/v4l2-compat-ioctl32.c @@ -1005,6 +1005,8 @@ long v4l2_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg) case VIDIOC_G_ENC_INDEX: case VIDIOC_ENCODER_CMD: case VIDIOC_TRY_ENCODER_CMD: + case VIDIOC_DECODER_CMD: + case VIDIOC_TRY_DECODER_CMD: case VIDIOC_DBG_S_REGISTER: case VIDIOC_DBG_G_REGISTER: case VIDIOC_DBG_G_CHIP_IDENT: diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c index d0d7281e01e0..a288ee6fd456 100644 --- a/drivers/media/video/v4l2-ioctl.c +++ b/drivers/media/video/v4l2-ioctl.c @@ -260,6 +260,8 @@ static const char *v4l2_ioctls[] = { [_IOC_NR(VIDIOC_ENCODER_CMD)] = "VIDIOC_ENCODER_CMD", [_IOC_NR(VIDIOC_TRY_ENCODER_CMD)] = "VIDIOC_TRY_ENCODER_CMD", + [_IOC_NR(VIDIOC_DECODER_CMD)] = "VIDIOC_DECODER_CMD", + [_IOC_NR(VIDIOC_TRY_DECODER_CMD)] = "VIDIOC_TRY_DECODER_CMD", [_IOC_NR(VIDIOC_DBG_S_REGISTER)] = "VIDIOC_DBG_S_REGISTER", [_IOC_NR(VIDIOC_DBG_G_REGISTER)] = "VIDIOC_DBG_G_REGISTER", @@ -1764,6 +1766,32 @@ static long __video_do_ioctl(struct file *file, dbgarg(cmd, "cmd=%d, flags=%x\n", p->cmd, p->flags); break; } + case VIDIOC_DECODER_CMD: + { + struct v4l2_decoder_cmd *p = arg; + + if (!ops->vidioc_decoder_cmd) + break; + if (ret_prio) { + ret = ret_prio; + break; + } + ret = ops->vidioc_decoder_cmd(file, fh, p); + if (!ret) + dbgarg(cmd, "cmd=%d, flags=%x\n", p->cmd, p->flags); + break; + } + case VIDIOC_TRY_DECODER_CMD: + { + struct v4l2_decoder_cmd *p = arg; + + if (!ops->vidioc_try_decoder_cmd) + break; + ret = ops->vidioc_try_decoder_cmd(file, fh, p); + if (!ret) + dbgarg(cmd, "cmd=%d, flags=%x\n", p->cmd, p->flags); + break; + } case VIDIOC_G_PARM: { struct v4l2_streamparm *p = arg; diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index 0db05033c2ec..e4ba8d77196d 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -1908,6 +1908,54 @@ struct v4l2_encoder_cmd { }; }; +/* Decoder commands */ +#define V4L2_DEC_CMD_START (0) +#define V4L2_DEC_CMD_STOP (1) +#define V4L2_DEC_CMD_PAUSE (2) +#define V4L2_DEC_CMD_RESUME (3) + +/* Flags for V4L2_DEC_CMD_START */ +#define V4L2_DEC_CMD_START_MUTE_AUDIO (1 << 0) + +/* Flags for V4L2_DEC_CMD_PAUSE */ +#define V4L2_DEC_CMD_PAUSE_TO_BLACK (1 << 0) + +/* Flags for V4L2_DEC_CMD_STOP */ +#define V4L2_DEC_CMD_STOP_TO_BLACK (1 << 0) +#define V4L2_DEC_CMD_STOP_IMMEDIATELY (1 << 1) + +/* Play format requirements (returned by the driver): */ + +/* The decoder has no special format requirements */ +#define V4L2_DEC_START_FMT_NONE (0) +/* The decoder requires full GOPs */ +#define V4L2_DEC_START_FMT_GOP (1) + +/* The structure must be zeroed before use by the application + This ensures it can be extended safely in the future. */ +struct v4l2_decoder_cmd { + __u32 cmd; + __u32 flags; + union { + struct { + __u64 pts; + } stop; + + struct { + /* 0 or 1000 specifies normal speed, + 1 specifies forward single stepping, + -1 specifies backward single stepping, + >1: playback at speed/1000 of the normal speed, + <-1: reverse playback at (-speed/1000) of the normal speed. */ + __s32 speed; + __u32 format; + } start; + + struct { + __u32 data[16]; + } raw; + }; +}; #endif @@ -2318,6 +2366,11 @@ struct v4l2_create_buffers { #define VIDIOC_G_SELECTION _IOWR('V', 94, struct v4l2_selection) #define VIDIOC_S_SELECTION _IOWR('V', 95, struct v4l2_selection) +/* Experimental, these two ioctls may change over the next couple of kernel + versions. */ +#define VIDIOC_DECODER_CMD _IOWR('V', 96, struct v4l2_decoder_cmd) +#define VIDIOC_TRY_DECODER_CMD _IOWR('V', 97, struct v4l2_decoder_cmd) + /* Reminder: when adding new ioctls please add support for them to drivers/media/video/v4l2-compat-ioctl32.c as well! */ diff --git a/include/media/v4l2-ioctl.h b/include/media/v4l2-ioctl.h index 3f5d60fc5df6..4df031af9949 100644 --- a/include/media/v4l2-ioctl.h +++ b/include/media/v4l2-ioctl.h @@ -211,6 +211,10 @@ struct v4l2_ioctl_ops { struct v4l2_encoder_cmd *a); int (*vidioc_try_encoder_cmd) (struct file *file, void *fh, struct v4l2_encoder_cmd *a); + int (*vidioc_decoder_cmd) (struct file *file, void *fh, + struct v4l2_decoder_cmd *a); + int (*vidioc_try_decoder_cmd) (struct file *file, void *fh, + struct v4l2_decoder_cmd *a); /* Stream type-dependent parameter ioctls */ int (*vidioc_g_parm) (struct file *file, void *fh, -- cgit v1.2.3 From 24c19a217e543b6b4a7715229f0e1fcf4bbd2659 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 15 Dec 2011 10:46:16 -0300 Subject: [media] v4l2-ctrls: add new controls for MPEG decoder devices As discussed during the 2011 V4L-DVB workshop we want to create a proper V4L2 decoder API that replaces the DVBv5 API that has been used until now. This adds the four controls necessary to be able to switch ivtv over to this new API. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/v4l2-ctrls.c | 23 +++++++++++++++++++++++ include/linux/videodev2.h | 13 +++++++++++++ 2 files changed, 36 insertions(+) (limited to 'include/linux') diff --git a/drivers/media/video/v4l2-ctrls.c b/drivers/media/video/v4l2-ctrls.c index cccd42be718a..94fbb5b50c74 100644 --- a/drivers/media/video/v4l2-ctrls.c +++ b/drivers/media/video/v4l2-ctrls.c @@ -175,6 +175,15 @@ const char * const *v4l2_ctrl_get_menu(u32 id) "16-bit CRC", NULL }; + static const char * const mpeg_audio_dec_playback[] = { + "Auto", + "Stereo", + "Left", + "Right", + "Mono", + "Swapped Stereo", + NULL + }; static const char * const mpeg_video_encoding[] = { "MPEG-1", "MPEG-2", @@ -374,6 +383,9 @@ const char * const *v4l2_ctrl_get_menu(u32 id) return mpeg_audio_emphasis; case V4L2_CID_MPEG_AUDIO_CRC: return mpeg_audio_crc; + case V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK: + case V4L2_CID_MPEG_AUDIO_DEC_MULTILINGUAL_PLAYBACK: + return mpeg_audio_dec_playback; case V4L2_CID_MPEG_VIDEO_ENCODING: return mpeg_video_encoding; case V4L2_CID_MPEG_VIDEO_ASPECT: @@ -492,6 +504,8 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_MPEG_AUDIO_MUTE: return "Audio Mute"; case V4L2_CID_MPEG_AUDIO_AAC_BITRATE: return "Audio AAC Bitrate"; case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: return "Audio AC-3 Bitrate"; + case V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK: return "Audio Playback"; + case V4L2_CID_MPEG_AUDIO_DEC_MULTILINGUAL_PLAYBACK: return "Audio Multilingual Playback"; case V4L2_CID_MPEG_VIDEO_ENCODING: return "Video Encoding"; case V4L2_CID_MPEG_VIDEO_ASPECT: return "Video Aspect"; case V4L2_CID_MPEG_VIDEO_B_FRAMES: return "Video B Frames"; @@ -546,6 +560,8 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB: return "Number of MBs in a Slice"; case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE: return "Slice Partitioning Method"; case V4L2_CID_MPEG_VIDEO_VBV_SIZE: return "VBV Buffer Size"; + case V4L2_CID_MPEG_VIDEO_DEC_PTS: return "Video Decoder PTS"; + case V4L2_CID_MPEG_VIDEO_DEC_FRAME: return "Video Decoder Frame Count"; /* CAMERA controls */ /* Keep the order of the 'case's the same as in videodev2.h! */ @@ -674,6 +690,8 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION: case V4L2_CID_MPEG_AUDIO_EMPHASIS: case V4L2_CID_MPEG_AUDIO_CRC: + case V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK: + case V4L2_CID_MPEG_AUDIO_DEC_MULTILINGUAL_PLAYBACK: case V4L2_CID_MPEG_VIDEO_ENCODING: case V4L2_CID_MPEG_VIDEO_ASPECT: case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: @@ -724,6 +742,11 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, *type = V4L2_CTRL_TYPE_INTEGER; *flags |= V4L2_CTRL_FLAG_READ_ONLY; break; + case V4L2_CID_MPEG_VIDEO_DEC_FRAME: + case V4L2_CID_MPEG_VIDEO_DEC_PTS: + *type = V4L2_CTRL_TYPE_INTEGER64; + *flags |= V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_VOLATILE; + break; default: *type = V4L2_CTRL_TYPE_INTEGER; break; diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index e4ba8d77196d..b739d7d6f7e7 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -1407,6 +1407,16 @@ enum v4l2_mpeg_audio_ac3_bitrate { V4L2_MPEG_AUDIO_AC3_BITRATE_576K = 17, V4L2_MPEG_AUDIO_AC3_BITRATE_640K = 18, }; +#define V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK (V4L2_CID_MPEG_BASE+112) +enum v4l2_mpeg_audio_dec_playback { + V4L2_MPEG_AUDIO_DEC_PLAYBACK_AUTO = 0, + V4L2_MPEG_AUDIO_DEC_PLAYBACK_STEREO = 1, + V4L2_MPEG_AUDIO_DEC_PLAYBACK_LEFT = 2, + V4L2_MPEG_AUDIO_DEC_PLAYBACK_RIGHT = 3, + V4L2_MPEG_AUDIO_DEC_PLAYBACK_MONO = 4, + V4L2_MPEG_AUDIO_DEC_PLAYBACK_SWAPPED_STEREO = 5, +}; +#define V4L2_CID_MPEG_AUDIO_DEC_MULTILINGUAL_PLAYBACK (V4L2_CID_MPEG_BASE+113) /* MPEG video controls specific to multiplexed streams */ #define V4L2_CID_MPEG_VIDEO_ENCODING (V4L2_CID_MPEG_BASE+200) @@ -1457,6 +1467,9 @@ enum v4l2_mpeg_video_multi_slice_mode { V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES = 2, }; #define V4L2_CID_MPEG_VIDEO_VBV_SIZE (V4L2_CID_MPEG_BASE+222) +#define V4L2_CID_MPEG_VIDEO_DEC_PTS (V4L2_CID_MPEG_BASE+223) +#define V4L2_CID_MPEG_VIDEO_DEC_FRAME (V4L2_CID_MPEG_BASE+224) + #define V4L2_CID_MPEG_VIDEO_H263_I_FRAME_QP (V4L2_CID_MPEG_BASE+300) #define V4L2_CID_MPEG_VIDEO_H263_P_FRAME_QP (V4L2_CID_MPEG_BASE+301) #define V4L2_CID_MPEG_VIDEO_H263_B_FRAME_QP (V4L2_CID_MPEG_BASE+302) -- cgit v1.2.3 From 6e82a6a2a0e895b787f22d28311232cc94fda4b9 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 15 Dec 2011 10:40:23 -0300 Subject: [media] ivtv: add IVTV_IOC_PASSTHROUGH_MODE With this private ioctl it is possible to use the ivtv decoder without requiring the dvb/video.h and dvb/audio.h headers. Eventually support for those DVB APIs will be dropped from ivtv. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/ivtv/ivtv-ioctl.c | 8 ++++++++ include/linux/ivtv.h | 6 +++++- 2 files changed, 13 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/drivers/media/video/ivtv/ivtv-ioctl.c b/drivers/media/video/ivtv/ivtv-ioctl.c index cd8032693e6d..37ac32b53baf 100644 --- a/drivers/media/video/ivtv/ivtv-ioctl.c +++ b/drivers/media/video/ivtv/ivtv-ioctl.c @@ -1625,6 +1625,12 @@ static int ivtv_decoder_ioctls(struct file *filp, unsigned int cmd, void *arg) return ivtv_yuv_prep_frame(itv, args); } + case IVTV_IOC_PASSTHROUGH_MODE: + IVTV_DEBUG_IOCTL("IVTV_IOC_PASSTHROUGH_MODE\n"); + if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) + return -EINVAL; + return ivtv_passthrough_mode(itv, *(int *)arg != 0); + case VIDEO_GET_PTS: { s64 *pts = arg; s64 frame; @@ -1786,6 +1792,7 @@ static long ivtv_default(struct file *file, void *fh, bool valid_prio, if (!valid_prio) { switch (cmd) { + case IVTV_IOC_PASSTHROUGH_MODE: case VIDEO_PLAY: case VIDEO_STOP: case VIDEO_FREEZE: @@ -1811,6 +1818,7 @@ static long ivtv_default(struct file *file, void *fh, bool valid_prio, } case IVTV_IOC_DMA_FRAME: + case IVTV_IOC_PASSTHROUGH_MODE: case VIDEO_GET_PTS: case VIDEO_GET_FRAME_COUNT: case VIDEO_GET_EVENT: diff --git a/include/linux/ivtv.h b/include/linux/ivtv.h index 062d20f74322..42bf725751af 100644 --- a/include/linux/ivtv.h +++ b/include/linux/ivtv.h @@ -58,7 +58,11 @@ struct ivtv_dma_frame { __u32 src_height; }; -#define IVTV_IOC_DMA_FRAME _IOW ('V', BASE_VIDIOC_PRIVATE+0, struct ivtv_dma_frame) +#define IVTV_IOC_DMA_FRAME _IOW ('V', BASE_VIDIOC_PRIVATE+0, struct ivtv_dma_frame) + +/* Select the passthrough mode (if the argument is non-zero). In the passthrough + mode the output of the encoder is passed immediately into the decoder. */ +#define IVTV_IOC_PASSTHROUGH_MODE _IOW ('V', BASE_VIDIOC_PRIVATE+1, int) /* Deprecated defines: applications should use the defines from videodev2.h */ #define IVTV_SLICED_TYPE_TELETEXT_B V4L2_MPEG_VBI_IVTV_TELETEXT_B -- cgit v1.2.3 From 925845bd49c6de437dfab3bf8dc654ea3ae21d74 Mon Sep 17 00:00:00 2001 From: Myron Stowe Date: Mon, 21 Nov 2011 11:54:13 -0700 Subject: x86/PCI: Infrastructure to maintain a list of FW-assigned BIOS BAR values Commit 58c84eda075 introduced functionality to try and reinstate the original BIOS BAR addresses of a PCI device when normal resource assignment attempts fail. To keep track of the BIOS BAR addresses, struct pci_dev was augmented with an array to hold the BAR addresses of the PCI device: 'resource_size_t fw_addr[DEVICE_COUNT_RESOURCE]'. The reinstatement of BAR addresses is an uncommon event leaving the 'fw_addr' array unused under normal circumstances. This functionality is also currently architecture specific with an implementation limited to x86. As the use of struct pci_dev is so prevalent, having the 'fw_addr' array residing within such seems somewhat wasteful. This patch introduces a stand alone data structure and interfacing routines for maintaining a list of FW-assigned BIOS BAR value entries. Signed-off-by: Myron Stowe Signed-off-by: Jesse Barnes --- arch/x86/pci/i386.c | 79 +++++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/pci.h | 1 + 2 files changed, 80 insertions(+) (limited to 'include/linux') diff --git a/arch/x86/pci/i386.c b/arch/x86/pci/i386.c index 91821a1a0c3a..5a1edf2b5386 100644 --- a/arch/x86/pci/i386.c +++ b/arch/x86/pci/i386.c @@ -39,6 +39,85 @@ #include +/* + * This list of dynamic mappings is for temporarily maintaining + * original BIOS BAR addresses for possible reinstatement. + */ +struct pcibios_fwaddrmap { + struct list_head list; + struct pci_dev *dev; + resource_size_t fw_addr[DEVICE_COUNT_RESOURCE]; +}; + +static LIST_HEAD(pcibios_fwaddrmappings); +static DEFINE_SPINLOCK(pcibios_fwaddrmap_lock); + +/* Must be called with 'pcibios_fwaddrmap_lock' lock held. */ +static struct pcibios_fwaddrmap *pcibios_fwaddrmap_lookup(struct pci_dev *dev) +{ + struct pcibios_fwaddrmap *map; + + list_for_each_entry(map, &pcibios_fwaddrmappings, list) + if (map->dev == dev) + return map; + + return NULL; +} + +static void +pcibios_save_fw_addr(struct pci_dev *dev, int idx, resource_size_t fw_addr) +{ + unsigned long flags; + struct pcibios_fwaddrmap *map; + + spin_lock_irqsave(&pcibios_fwaddrmap_lock, flags); + map = pcibios_fwaddrmap_lookup(dev); + if (!map) { + spin_unlock_irqrestore(&pcibios_fwaddrmap_lock, flags); + map = kzalloc(sizeof(*map), GFP_KERNEL); + if (!map) + return; + + map->dev = pci_dev_get(dev); + map->fw_addr[idx] = fw_addr; + INIT_LIST_HEAD(&map->list); + + spin_lock_irqsave(&pcibios_fwaddrmap_lock, flags); + list_add_tail(&map->list, &pcibios_fwaddrmappings); + } else + map->fw_addr[idx] = fw_addr; + spin_unlock_irqrestore(&pcibios_fwaddrmap_lock, flags); +} + +resource_size_t pcibios_retrieve_fw_addr(struct pci_dev *dev, int idx) +{ + unsigned long flags; + struct pcibios_fwaddrmap *map; + resource_size_t fw_addr = 0; + + spin_lock_irqsave(&pcibios_fwaddrmap_lock, flags); + map = pcibios_fwaddrmap_lookup(dev); + if (map) + fw_addr = map->fw_addr[idx]; + spin_unlock_irqrestore(&pcibios_fwaddrmap_lock, flags); + + return fw_addr; +} + +static void pcibios_fw_addr_list_del(void) +{ + unsigned long flags; + struct pcibios_fwaddrmap *entry, *next; + + spin_lock_irqsave(&pcibios_fwaddrmap_lock, flags); + list_for_each_entry_safe(entry, next, &pcibios_fwaddrmappings, list) { + list_del(&entry->list); + pci_dev_put(entry->dev); + kfree(entry); + } + spin_unlock_irqrestore(&pcibios_fwaddrmap_lock, flags); +} + static int skip_isa_ioresource_align(struct pci_dev *dev) { diff --git a/include/linux/pci.h b/include/linux/pci.h index a16b1df3deff..8e9a307e58b8 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -891,6 +891,7 @@ ssize_t pci_write_vpd(struct pci_dev *dev, loff_t pos, size_t count, const void int pci_vpd_truncate(struct pci_dev *dev, size_t size); /* Helper functions for low-level code (drivers/pci/setup-[bus,res].c) */ +resource_size_t pcibios_retrieve_fw_addr(struct pci_dev *dev, int idx); void pci_bus_assign_resources(const struct pci_bus *bus); void pci_bus_size_bridges(struct pci_bus *bus); int pci_claim_resource(struct pci_dev *, int); -- cgit v1.2.3 From 6535943fbf25c8e9419a6b20ca992633baa0bf99 Mon Sep 17 00:00:00 2001 From: Myron Stowe Date: Mon, 21 Nov 2011 11:54:19 -0700 Subject: x86/PCI: Convert maintaining FW-assigned BIOS BAR values to use a list This patch converts the underlying maintenance aspects of FW-assigned BIOS BAR values from a statically allocated array within struct pci_dev to a list of temporary, stand alone, entries. Signed-off-by: Myron Stowe Signed-off-by: Jesse Barnes --- arch/x86/pci/i386.c | 4 +++- drivers/pci/setup-res.c | 24 +++++++++++++++++++++--- include/linux/pci.h | 1 - 3 files changed, 24 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/pci/i386.c b/arch/x86/pci/i386.c index 5a1edf2b5386..33e6a0b995fc 100644 --- a/arch/x86/pci/i386.c +++ b/arch/x86/pci/i386.c @@ -261,7 +261,8 @@ static void __init pcibios_allocate_resources(int pass) idx, r, disabled, pass); if (pci_claim_resource(dev, idx) < 0) { /* We'll assign a new address later */ - dev->fw_addr[idx] = r->start; + pcibios_save_fw_addr(dev, + idx, r->start); r->end -= r->start; r->start = 0; } @@ -307,6 +308,7 @@ static int __init pcibios_assign_resources(void) } pci_assign_unassigned_resources(); + pcibios_fw_addr_list_del(); return 0; } diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index 3cf47d34becf..85c8470c35e2 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -158,16 +158,34 @@ static int __pci_assign_resource(struct pci_bus *bus, struct pci_dev *dev, return ret; } +/* + * Generic function that returns a value indicating that the device's + * original BIOS BAR address was not saved and so is not available for + * reinstatement. + * + * Can be over-ridden by architecture specific code that implements + * reinstatement functionality rather than leaving it disabled when + * normal allocation attempts fail. + */ +resource_size_t __weak pcibios_retrieve_fw_addr(struct pci_dev *dev, int idx) +{ + return 0; +} + static int pci_revert_fw_address(struct resource *res, struct pci_dev *dev, int resno, resource_size_t size) { struct resource *root, *conflict; - resource_size_t start, end; + resource_size_t fw_addr, start, end; int ret = 0; + fw_addr = pcibios_retrieve_fw_addr(dev, resno); + if (!fw_addr) + return 1; + start = res->start; end = res->end; - res->start = dev->fw_addr[resno]; + res->start = fw_addr; res->end = res->start + size - 1; root = pci_find_parent_resource(dev, res); @@ -271,7 +289,7 @@ int pci_assign_resource(struct pci_dev *dev, int resno) * where firmware left it. That at least has a chance of * working, which is better than just leaving it disabled. */ - if (ret < 0 && dev->fw_addr[resno]) + if (ret < 0) ret = pci_revert_fw_address(res, dev, resno, size); if (!ret) { diff --git a/include/linux/pci.h b/include/linux/pci.h index 8e9a307e58b8..4afabb1d2d27 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -299,7 +299,6 @@ struct pci_dev { */ unsigned int irq; struct resource resource[DEVICE_COUNT_RESOURCE]; /* I/O and memory regions + expansion ROMs */ - resource_size_t fw_addr[DEVICE_COUNT_RESOURCE]; /* FW-assigned addr */ /* These fields are used by common fixups */ unsigned int transparent:1; /* Transparent PCI bridge */ -- cgit v1.2.3 From 6fbf9e7a90862988c278462d85ce9684605a52b2 Mon Sep 17 00:00:00 2001 From: Konrad Rzeszutek Wilk Date: Thu, 12 Jan 2012 12:06:46 -0500 Subject: PCI: Introduce __pci_reset_function_locked to be used when holding device_lock. The use case of this is when a driver wants to call FLR when a device is attached to it using the SysFS "bind" or "unbind" functionality. The call chain when a user does "bind" looks as so: echo "0000:01.07.0" > /sys/bus/pci/drivers/XXXX/bind and ends up calling: driver_bind: device_lock(dev); <=== TAKES LOCK XXXX_probe: .. pci_enable_device() ...__pci_reset_function(), which calls pci_dev_reset(dev, 0): if (!0) { device_lock(dev) <==== DEADLOCK The __pci_reset_function_locked function allows the the drivers 'probe' function to call the "pci_reset_function" while still holding the driver mutex lock. Signed-off-by: Konrad Rzeszutek Wilk Signed-off-by: Jesse Barnes --- drivers/pci/pci.c | 25 +++++++++++++++++++++++++ include/linux/pci.h | 1 + 2 files changed, 26 insertions(+) (limited to 'include/linux') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index af295bb21d62..053670e09e2b 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -3162,6 +3162,31 @@ int __pci_reset_function(struct pci_dev *dev) } EXPORT_SYMBOL_GPL(__pci_reset_function); +/** + * __pci_reset_function_locked - reset a PCI device function while holding + * the @dev mutex lock. + * @dev: PCI device to reset + * + * Some devices allow an individual function to be reset without affecting + * other functions in the same device. The PCI device must be responsive + * to PCI config space in order to use this function. + * + * The device function is presumed to be unused and the caller is holding + * the device mutex lock when this function is called. + * Resetting the device will make the contents of PCI configuration space + * random, so any caller of this must be prepared to reinitialise the + * device including MSI, bus mastering, BARs, decoding IO and memory spaces, + * etc. + * + * Returns 0 if the device function was successfully reset or negative if the + * device doesn't support resetting a single function. + */ +int __pci_reset_function_locked(struct pci_dev *dev) +{ + return pci_dev_reset(dev, 1); +} +EXPORT_SYMBOL_GPL(__pci_reset_function_locked); + /** * pci_probe_reset_function - check whether the device can be safely reset * @dev: PCI device to reset diff --git a/include/linux/pci.h b/include/linux/pci.h index 4afabb1d2d27..f44276049f4a 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -816,6 +816,7 @@ int pcie_set_readrq(struct pci_dev *dev, int rq); int pcie_get_mps(struct pci_dev *dev); int pcie_set_mps(struct pci_dev *dev, int mps); int __pci_reset_function(struct pci_dev *dev); +int __pci_reset_function_locked(struct pci_dev *dev); int pci_reset_function(struct pci_dev *dev); void pci_update_resource(struct pci_dev *dev, int resno); int __must_check pci_assign_resource(struct pci_dev *dev, int i); -- cgit v1.2.3 From 2f320521a0d2d11fb857be09d05e2fbbf3ef8c13 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Sat, 21 Jan 2012 02:08:22 -0800 Subject: PCI: Make rescan bus increase bridge resource size if needed Current rescan will not touch bridge MMIO and IO. Try to reuse pci_assign_unassigned_bridge_resources(bridge) to update bridge resources, if child devices need more resources. Only do that for bridges whose children are all removed already; i.e. don't release resources that could already be in use by drivers on child devices. Signed-off-by: Yinghai Lu Signed-off-by: Jesse Barnes --- drivers/pci/pci-sysfs.c | 5 ++++- drivers/pci/probe.c | 25 +++++++++++++++++++++++++ include/linux/pci.h | 1 + 3 files changed, 30 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index a3cd8cad532a..d9723039e99a 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -366,7 +366,10 @@ dev_bus_rescan_store(struct device *dev, struct device_attribute *attr, if (val) { mutex_lock(&pci_remove_rescan_mutex); - pci_rescan_bus(bus); + if (!pci_is_root_bus(bus) && list_empty(&bus->devices)) + pci_rescan_bus_bridge_resize(bus->self); + else + pci_rescan_bus(bus); mutex_unlock(&pci_remove_rescan_mutex); } return count; diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 71eac9cd724d..0e84e8c2a6d0 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1666,6 +1666,31 @@ struct pci_bus * __devinit pci_scan_bus(int bus, struct pci_ops *ops, EXPORT_SYMBOL(pci_scan_bus); #ifdef CONFIG_HOTPLUG +/** + * pci_rescan_bus_bridge_resize - scan a PCI bus for devices. + * @bridge: PCI bridge for the bus to scan + * + * Scan a PCI bus and child buses for new devices, add them, + * and enable them, resizing bridge mmio/io resource if necessary + * and possible. The caller must ensure the child devices are already + * removed for resizing to occur. + * + * Returns the max number of subordinate bus discovered. + */ +unsigned int __ref pci_rescan_bus_bridge_resize(struct pci_dev *bridge) +{ + unsigned int max; + struct pci_bus *bus = bridge->subordinate; + + max = pci_scan_child_bus(bus); + + pci_assign_unassigned_bridge_resources(bridge); + + pci_bus_add_devices(bus); + + return max; +} + /** * pci_rescan_bus - scan a PCI bus for devices. * @bus: PCI bus to scan diff --git a/include/linux/pci.h b/include/linux/pci.h index f44276049f4a..87507aadf9a2 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -882,6 +882,7 @@ void set_pcie_hotplug_bridge(struct pci_dev *pdev); /* Functions for PCI Hotplug drivers to use */ int pci_bus_find_capability(struct pci_bus *bus, unsigned int devfn, int cap); #ifdef CONFIG_HOTPLUG +unsigned int pci_rescan_bus_bridge_resize(struct pci_dev *bridge); unsigned int pci_rescan_bus(struct pci_bus *bus); #endif -- cgit v1.2.3 From 78c3b329b9dd7097781cb900146e503e499cccfe Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Sat, 21 Jan 2012 02:08:25 -0800 Subject: PCI: Move pdev_sort_resources() to setup-bus.c This allows us to move the definition of struct resource_list to setup_bus.c and later convert resource_list to a regular list. Suggested-by: Linus Torvalds Signed-off-by: Yinghai Lu Signed-off-by: Jesse Barnes --- drivers/pci/setup-bus.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/pci/setup-res.c | 47 ----------------------------------------------- include/linux/pci.h | 1 - 3 files changed, 46 insertions(+), 48 deletions(-) (limited to 'include/linux') diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index c79ce4ee634b..f233d127ca89 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -137,6 +137,52 @@ static resource_size_t get_res_add_size(struct resource_list_x *realloc_head, return 0; } +/* Sort resources by alignment */ +static void pdev_sort_resources(struct pci_dev *dev, struct resource_list *head) +{ + int i; + + for (i = 0; i < PCI_NUM_RESOURCES; i++) { + struct resource *r; + struct resource_list *list, *tmp; + resource_size_t r_align; + + r = &dev->resource[i]; + + if (r->flags & IORESOURCE_PCI_FIXED) + continue; + + if (!(r->flags) || r->parent) + continue; + + r_align = pci_resource_alignment(dev, r); + if (!r_align) { + dev_warn(&dev->dev, "BAR %d: %pR has bogus alignment\n", + i, r); + continue; + } + for (list = head; ; list = list->next) { + resource_size_t align = 0; + struct resource_list *ln = list->next; + + if (ln) + align = pci_resource_alignment(ln->dev, ln->res); + + if (r_align > align) { + tmp = kmalloc(sizeof(*tmp), GFP_KERNEL); + if (!tmp) + panic("pdev_sort_resources(): " + "kmalloc() failed!\n"); + tmp->next = ln; + tmp->res = r; + tmp->dev = dev; + list->next = tmp; + break; + } + } + } +} + static void __dev_sort_resources(struct pci_dev *dev, struct resource_list *head) { diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index 241de6c2b9cd..f968185aa192 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -302,53 +302,6 @@ int pci_assign_resource(struct pci_dev *dev, int resno) return ret; } - -/* Sort resources by alignment */ -void pdev_sort_resources(struct pci_dev *dev, struct resource_list *head) -{ - int i; - - for (i = 0; i < PCI_NUM_RESOURCES; i++) { - struct resource *r; - struct resource_list *list, *tmp; - resource_size_t r_align; - - r = &dev->resource[i]; - - if (r->flags & IORESOURCE_PCI_FIXED) - continue; - - if (!(r->flags) || r->parent) - continue; - - r_align = pci_resource_alignment(dev, r); - if (!r_align) { - dev_warn(&dev->dev, "BAR %d: %pR has bogus alignment\n", - i, r); - continue; - } - for (list = head; ; list = list->next) { - resource_size_t align = 0; - struct resource_list *ln = list->next; - - if (ln) - align = pci_resource_alignment(ln->dev, ln->res); - - if (r_align > align) { - tmp = kmalloc(sizeof(*tmp), GFP_KERNEL); - if (!tmp) - panic("pdev_sort_resources(): " - "kmalloc() failed!\n"); - tmp->next = ln; - tmp->res = r; - tmp->dev = dev; - list->next = tmp; - break; - } - } - } -} - int pci_enable_resources(struct pci_dev *dev, int mask) { u16 cmd, old_cmd; diff --git a/include/linux/pci.h b/include/linux/pci.h index 87507aadf9a2..f8caaab4b3fc 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -899,7 +899,6 @@ int pci_claim_resource(struct pci_dev *, int); void pci_assign_unassigned_resources(void); void pci_assign_unassigned_bridge_resources(struct pci_dev *bridge); void pdev_enable_device(struct pci_dev *); -void pdev_sort_resources(struct pci_dev *, struct resource_list *); int pci_enable_resources(struct pci_dev *, int mask); void pci_fixup_irqs(u8 (*)(struct pci_dev *, u8 *), int (*)(const struct pci_dev *, u8, u8)); -- cgit v1.2.3 From 2934a0de095f277a7bbc15a72ecf61af31a45163 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Sat, 21 Jan 2012 02:08:26 -0800 Subject: PCI: Move struct resource_list to setup-bus.c No user outside of setup-bus.c now. Later patches will convert resource_list to a regular list. Suggested-by: Linus Torvalds Signed-off-by: Yinghai Lu Signed-off-by: Jesse Barnes --- drivers/pci/setup-bus.c | 6 ++++++ include/linux/ioport.h | 6 ------ 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index f233d127ca89..b067a4cdce43 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -27,6 +27,12 @@ #include #include "pci.h" +struct resource_list { + struct resource_list *next; + struct resource *res; + struct pci_dev *dev; +}; + struct resource_list_x { struct resource_list_x *next; struct resource *res; diff --git a/include/linux/ioport.h b/include/linux/ioport.h index 9d57a71775b5..e885ba23de70 100644 --- a/include/linux/ioport.h +++ b/include/linux/ioport.h @@ -23,12 +23,6 @@ struct resource { struct resource *parent, *sibling, *child; }; -struct resource_list { - struct resource_list *next; - struct resource *res; - struct pci_dev *dev; -}; - /* * IO resources have these defined flags. */ -- cgit v1.2.3 From 8eaeb21925563075ae036c2e5ba8d041b70e18fa Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Sun, 12 Feb 2012 19:49:43 +0530 Subject: regmap: add regmap_bulk_write() for register write The bulk_write() supports the data transfer to multi register which takes the data into cpu_endianness format and does formatting of data to device format before sending to device. The transfer can be completed in single transfer or multiple transfer based on data formatting. Signed-off-by: Laxman Dewangan Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 50 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/regmap.h | 2 ++ 2 files changed, 52 insertions(+) (limited to 'include/linux') diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 74a6d4f8e3b8..2366b6299f07 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -473,6 +473,56 @@ int regmap_raw_write(struct regmap *map, unsigned int reg, } EXPORT_SYMBOL_GPL(regmap_raw_write); +/* + * regmap_bulk_write(): Write multiple registers to the device + * + * @map: Register map to write to + * @reg: First register to be write from + * @val: Block of data to be written, in native register size for device + * @val_count: Number of registers to write + * + * This function is intended to be used for writing a large block of + * data to be device either in single transfer or multiple transfer. + * + * A value of zero will be returned on success, a negative errno will + * be returned in error cases. + */ +int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, + size_t val_count) +{ + int ret = 0, i; + size_t val_bytes = map->format.val_bytes; + void *wval; + + if (!map->format.parse_val) + return -EINVAL; + + mutex_lock(&map->lock); + + /* No formatting is require if val_byte is 1 */ + if (val_bytes == 1) { + wval = (void *)val; + } else { + wval = kmemdup(val, val_count * val_bytes, GFP_KERNEL); + if (!wval) { + ret = -ENOMEM; + dev_err(map->dev, "Error in memory allocation\n"); + goto out; + } + for (i = 0; i < val_count * val_bytes; i += val_bytes) + map->format.parse_val(wval + i); + } + ret = _regmap_raw_write(map, reg, wval, val_bytes * val_count); + + if (val_bytes != 1) + kfree(wval); + +out: + mutex_unlock(&map->lock); + return ret; +} +EXPORT_SYMBOL_GPL(regmap_bulk_write); + static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val, unsigned int val_len) { diff --git a/include/linux/regmap.h b/include/linux/regmap.h index eb93921cdd30..1859793bc4c0 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -133,6 +133,8 @@ int regmap_reinit_cache(struct regmap *map, int regmap_write(struct regmap *map, unsigned int reg, unsigned int val); int regmap_raw_write(struct regmap *map, unsigned int reg, const void *val, size_t val_len); +int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, + size_t val_count); int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val); int regmap_raw_read(struct regmap *map, unsigned int reg, void *val, size_t val_len); -- cgit v1.2.3 From 45d43c291e9a922d7b432b0dbcb1d8fb70d8410f Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Mon, 6 Feb 2012 19:38:51 -0500 Subject: NFSv4.1: Convert slotid from u8 to u32 It is perfectly legal to negotiate up to 2^32-1 slots in the protocol, and with 10GigE, we are already seeing that 255 slots is far too limiting. Signed-off-by: Trond Myklebust --- fs/nfs/callback.h | 2 +- fs/nfs/callback_xdr.c | 6 +++--- fs/nfs/nfs4proc.c | 42 ++++++++++++++++++++---------------------- include/linux/nfs_fs_sb.h | 7 ++++--- include/linux/nfs_xdr.h | 2 +- 5 files changed, 29 insertions(+), 30 deletions(-) (limited to 'include/linux') diff --git a/fs/nfs/callback.h b/fs/nfs/callback.h index 197e0d3754c2..a5527c90a5aa 100644 --- a/fs/nfs/callback.h +++ b/fs/nfs/callback.h @@ -38,7 +38,7 @@ enum nfs4_callback_opnum { struct cb_process_state { __be32 drc_status; struct nfs_client *clp; - int slotid; + u32 slotid; struct net *net; }; diff --git a/fs/nfs/callback_xdr.c b/fs/nfs/callback_xdr.c index 2e372240d028..5466829c7e77 100644 --- a/fs/nfs/callback_xdr.c +++ b/fs/nfs/callback_xdr.c @@ -759,14 +759,14 @@ static void nfs4_callback_free_slot(struct nfs4_session *session) * Let the state manager know callback processing done. * A single slot, so highest used slotid is either 0 or -1 */ - tbl->highest_used_slotid = -1; + tbl->highest_used_slotid = NFS4_NO_SLOT; nfs4_check_drain_bc_complete(session); spin_unlock(&tbl->slot_tbl_lock); } static void nfs4_cb_free_slot(struct cb_process_state *cps) { - if (cps->slotid != -1) + if (cps->slotid != NFS4_NO_SLOT) nfs4_callback_free_slot(cps->clp->cl_session); } @@ -860,7 +860,7 @@ static __be32 nfs4_callback_compound(struct svc_rqst *rqstp, void *argp, void *r struct cb_process_state cps = { .drc_status = 0, .clp = NULL, - .slotid = -1, + .slotid = NFS4_NO_SLOT, .net = rqstp->rq_xprt->xpt_net, }; unsigned int nops = 0; diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 482ed97189c9..f3f56f4a3b72 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -360,16 +360,14 @@ static void renew_lease(const struct nfs_server *server, unsigned long timestamp * When updating highest_used_slotid there may be "holes" in the bitmap * so we need to scan down from highest_used_slotid to 0 looking for the now * highest slotid in use. - * If none found, highest_used_slotid is set to -1. + * If none found, highest_used_slotid is set to NFS4_NO_SLOT. * * Must be called while holding tbl->slot_tbl_lock */ static void -nfs4_free_slot(struct nfs4_slot_table *tbl, u8 free_slotid) +nfs4_free_slot(struct nfs4_slot_table *tbl, u32 slotid) { - int slotid = free_slotid; - - BUG_ON(slotid < 0 || slotid >= NFS4_MAX_SLOT_TABLE); + BUG_ON(slotid >= NFS4_MAX_SLOT_TABLE); /* clear used bit in bitmap */ __clear_bit(slotid, tbl->used_slots); @@ -379,10 +377,10 @@ nfs4_free_slot(struct nfs4_slot_table *tbl, u8 free_slotid) if (slotid < tbl->max_slots) tbl->highest_used_slotid = slotid; else - tbl->highest_used_slotid = -1; + tbl->highest_used_slotid = NFS4_NO_SLOT; } - dprintk("%s: free_slotid %u highest_used_slotid %d\n", __func__, - free_slotid, tbl->highest_used_slotid); + dprintk("%s: slotid %u highest_used_slotid %d\n", __func__, + slotid, tbl->highest_used_slotid); } bool nfs4_set_task_privileged(struct rpc_task *task, void *dummy) @@ -402,7 +400,7 @@ static void nfs4_check_drain_fc_complete(struct nfs4_session *ses) return; } - if (ses->fc_slot_table.highest_used_slotid != -1) + if (ses->fc_slot_table.highest_used_slotid != NFS4_NO_SLOT) return; dprintk("%s COMPLETE: Session Fore Channel Drained\n", __func__); @@ -415,7 +413,7 @@ static void nfs4_check_drain_fc_complete(struct nfs4_session *ses) void nfs4_check_drain_bc_complete(struct nfs4_session *ses) { if (!test_bit(NFS4_SESSION_DRAINING, &ses->session_state) || - ses->bc_slot_table.highest_used_slotid != -1) + ses->bc_slot_table.highest_used_slotid != NFS4_NO_SLOT) return; dprintk("%s COMPLETE: Session Back Channel Drained\n", __func__); complete(&ses->bc_slot_table.complete); @@ -510,25 +508,25 @@ static int nfs4_sequence_done(struct rpc_task *task, * nfs4_find_slot looks for an unset bit in the used_slots bitmap. * If found, we mark the slot as used, update the highest_used_slotid, * and respectively set up the sequence operation args. - * The slot number is returned if found, or NFS4_MAX_SLOT_TABLE otherwise. + * The slot number is returned if found, or NFS4_NO_SLOT otherwise. * * Note: must be called with under the slot_tbl_lock. */ -static u8 +static u32 nfs4_find_slot(struct nfs4_slot_table *tbl) { - int slotid; - u8 ret_id = NFS4_MAX_SLOT_TABLE; - BUILD_BUG_ON((u8)NFS4_MAX_SLOT_TABLE != (int)NFS4_MAX_SLOT_TABLE); + u32 slotid; + u32 ret_id = NFS4_NO_SLOT; - dprintk("--> %s used_slots=%04lx highest_used=%d max_slots=%d\n", + dprintk("--> %s used_slots=%04lx highest_used=%u max_slots=%u\n", __func__, tbl->used_slots[0], tbl->highest_used_slotid, tbl->max_slots); slotid = find_first_zero_bit(tbl->used_slots, tbl->max_slots); if (slotid >= tbl->max_slots) goto out; __set_bit(slotid, tbl->used_slots); - if (slotid > tbl->highest_used_slotid) + if (slotid > tbl->highest_used_slotid || + tbl->highest_used_slotid == NFS4_NO_SLOT) tbl->highest_used_slotid = slotid; ret_id = slotid; out: @@ -555,7 +553,7 @@ int nfs41_setup_sequence(struct nfs4_session *session, { struct nfs4_slot *slot; struct nfs4_slot_table *tbl; - u8 slotid; + u32 slotid; dprintk("--> %s\n", __func__); /* slot already allocated? */ @@ -583,7 +581,7 @@ int nfs41_setup_sequence(struct nfs4_session *session, } slotid = nfs4_find_slot(tbl); - if (slotid == NFS4_MAX_SLOT_TABLE) { + if (slotid == NFS4_NO_SLOT) { rpc_sleep_on(&tbl->slot_tbl_waitq, task, NULL); spin_unlock(&tbl->slot_tbl_lock); dprintk("<-- %s: no free slots\n", __func__); @@ -5144,7 +5142,7 @@ static int nfs4_init_slot_table(struct nfs4_slot_table *tbl, spin_lock(&tbl->slot_tbl_lock); tbl->max_slots = max_slots; tbl->slots = slot; - tbl->highest_used_slotid = -1; /* no slot is currently used */ + tbl->highest_used_slotid = NFS4_NO_SLOT; /* no slot is currently used */ spin_unlock(&tbl->slot_tbl_lock); dprintk("%s: tbl=%p slots=%p max_slots=%d\n", __func__, tbl, tbl->slots, tbl->max_slots); @@ -5196,13 +5194,13 @@ struct nfs4_session *nfs4_alloc_session(struct nfs_client *clp) return NULL; tbl = &session->fc_slot_table; - tbl->highest_used_slotid = -1; + tbl->highest_used_slotid = NFS4_NO_SLOT; spin_lock_init(&tbl->slot_tbl_lock); rpc_init_priority_wait_queue(&tbl->slot_tbl_waitq, "ForeChannel Slot table"); init_completion(&tbl->complete); tbl = &session->bc_slot_table; - tbl->highest_used_slotid = -1; + tbl->highest_used_slotid = NFS4_NO_SLOT; spin_lock_init(&tbl->slot_tbl_lock); rpc_init_wait_queue(&tbl->slot_tbl_waitq, "BackChannel Slot table"); init_completion(&tbl->complete); diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h index 9e101c1b81cf..2ae57f2f645b 100644 --- a/include/linux/nfs_fs_sb.h +++ b/include/linux/nfs_fs_sb.h @@ -191,6 +191,7 @@ struct nfs_server { /* maximum number of slots to use */ #define NFS4_MAX_SLOT_TABLE (128U) +#define NFS4_NO_SLOT ((u32)-1) #if defined(CONFIG_NFS_V4) @@ -201,10 +202,10 @@ struct nfs4_slot_table { unsigned long used_slots[SLOT_TABLE_SZ]; /* used/unused bitmap */ spinlock_t slot_tbl_lock; struct rpc_wait_queue slot_tbl_waitq; /* allocators may wait here */ - int max_slots; /* # slots in table */ - int highest_used_slotid; /* sent to server on each SEQ. + u32 max_slots; /* # slots in table */ + u32 highest_used_slotid; /* sent to server on each SEQ. * op for dynamic resizing */ - int target_max_slots; /* Set by CB_RECALL_SLOT as + u32 target_max_slots; /* Set by CB_RECALL_SLOT as * the new max_slots */ struct completion complete; }; diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 144419a9cbd3..adbc84ac345f 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -181,7 +181,7 @@ struct nfs4_slot { struct nfs4_sequence_args { struct nfs4_session *sa_session; - u8 sa_slotid; + u32 sa_slotid; u8 sa_cache_this; }; -- cgit v1.2.3 From ef159e9177cc5a09e6174796dde0b2d243ddf28b Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Mon, 6 Feb 2012 19:50:40 -0500 Subject: NFSv4.1: Add a module parameter to set the number of session slots Add the module parameter 'max_session_slots' to set the initial number of slots that the NFSv4.1 client will attempt to negotiate with the server. Signed-off-by: Trond Myklebust --- Documentation/kernel-parameters.txt | 8 ++++++++ fs/nfs/nfs4proc.c | 8 +++++++- include/linux/nfs_fs_sb.h | 5 +++-- 3 files changed, 18 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 033d4e69b43b..1d369c6286e1 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -1657,6 +1657,14 @@ bytes respectively. Such letter suffixes can also be entirely omitted. of returning the full 64-bit number. The default is to return 64-bit inode numbers. + nfs.max_session_slots= + [NFSv4.1] Sets the maximum number of session slots + the client will attempt to negotiate with the server. + This limits the number of simultaneous RPC requests + that the client can send to the NFSv4.1 server. + Note that there is little point in setting this + value higher than the max_tcp_slot_table_limit. + nfs.nfs4_disable_idmapping= [NFSv4] When set to the default of '1', this option ensures that both the RPC level authentication diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index f3f56f4a3b72..0b3316541734 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -72,6 +72,8 @@ #define NFS4_MAX_LOOP_ON_RECOVER (10) +static unsigned short max_session_slots = NFS4_DEF_SLOT_TABLE_SIZE; + struct nfs4_opendata; static int _nfs4_proc_open(struct nfs4_opendata *data); static int _nfs4_recover_proc_open(struct nfs4_opendata *data); @@ -5245,7 +5247,7 @@ static void nfs4_init_channel_attrs(struct nfs41_create_session_args *args) args->fc_attrs.max_rqst_sz = mxrqst_sz; args->fc_attrs.max_resp_sz = mxresp_sz; args->fc_attrs.max_ops = NFS4_MAX_OPS; - args->fc_attrs.max_reqs = session->clp->cl_rpcclient->cl_xprt->max_reqs; + args->fc_attrs.max_reqs = max_session_slots; dprintk("%s: Fore Channel : max_rqst_sz=%u max_resp_sz=%u " "max_ops=%u max_reqs=%u\n", @@ -6390,6 +6392,10 @@ const struct xattr_handler *nfs4_xattr_handlers[] = { NULL }; +module_param(max_session_slots, ushort, 0644); +MODULE_PARM_DESC(max_session_slots, "Maximum number of outstanding NFSv4.1 " + "requests the client will negotiate"); + /* * Local variables: * c-basic-offset: 8 diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h index 2ae57f2f645b..3bf47666646e 100644 --- a/include/linux/nfs_fs_sb.h +++ b/include/linux/nfs_fs_sb.h @@ -190,13 +190,14 @@ struct nfs_server { /* maximum number of slots to use */ -#define NFS4_MAX_SLOT_TABLE (128U) +#define NFS4_DEF_SLOT_TABLE_SIZE (16U) +#define NFS4_MAX_SLOT_TABLE (256U) #define NFS4_NO_SLOT ((u32)-1) #if defined(CONFIG_NFS_V4) /* Sessions */ -#define SLOT_TABLE_SZ (NFS4_MAX_SLOT_TABLE/(8*sizeof(long))) +#define SLOT_TABLE_SZ DIV_ROUND_UP(NFS4_MAX_SLOT_TABLE, 8*sizeof(long)) struct nfs4_slot_table { struct nfs4_slot *slots; /* seqid per slot */ unsigned long used_slots[SLOT_TABLE_SZ]; /* used/unused bitmap */ -- cgit v1.2.3 From 7b147f1ff267d12e0d189ca3d4156ed5a76b8d99 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Tue, 31 Jan 2012 14:09:17 +0400 Subject: SUNRPC: service destruction in network namespace context v2: Added comment to BUG_ON's in svc_destroy() to make code looks clearer. This patch introduces network namespace filter for service destruction function. Nothing special here - just do exactly the same operations, but only for tranports in passed networks namespace context. BTW, BUG_ON() checks for empty service transports lists were returned into svc_destroy() function. This is because of swithing generic svc_close_all() to networks namespace dependable svc_close_net(). Signed-off-by: Stanislav Kinsbursky Signed-off-by: Trond Myklebust --- include/linux/sunrpc/svcsock.h | 2 +- net/sunrpc/svc.c | 13 +++++++++++-- net/sunrpc/svc_xprt.c | 27 +++++++++++++++++---------- 3 files changed, 29 insertions(+), 13 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sunrpc/svcsock.h b/include/linux/sunrpc/svcsock.h index c84e9741cb2a..cb4ac69e1f33 100644 --- a/include/linux/sunrpc/svcsock.h +++ b/include/linux/sunrpc/svcsock.h @@ -34,7 +34,7 @@ struct svc_sock { /* * Function prototypes. */ -void svc_close_all(struct svc_serv *); +void svc_close_net(struct svc_serv *, struct net *); int svc_recv(struct svc_rqst *, long); int svc_send(struct svc_rqst *); void svc_drop(struct svc_rqst *); diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index a8b49a044619..6cc0ea3d26f1 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -517,6 +517,8 @@ EXPORT_SYMBOL_GPL(svc_create_pooled); void svc_destroy(struct svc_serv *serv) { + struct net *net = current->nsproxy->net_ns; + dprintk("svc: svc_destroy(%s, %d)\n", serv->sv_program->pg_name, serv->sv_nrthreads); @@ -539,10 +541,17 @@ svc_destroy(struct svc_serv *serv) * caller is using--nfsd_mutex in the case of nfsd). So it's * safe to traverse those lists and shut everything down: */ - svc_close_all(serv); + svc_close_net(serv, net); + + /* + * The last user is gone and thus all sockets have to be destroyed to + * the point. Check this. + */ + BUG_ON(!list_empty(&serv->sv_permsocks)); + BUG_ON(!list_empty(&serv->sv_tempsocks)); if (serv->sv_shutdown) - serv->sv_shutdown(serv, current->nsproxy->net_ns); + serv->sv_shutdown(serv, net); cache_clean_deferred(serv); diff --git a/net/sunrpc/svc_xprt.c b/net/sunrpc/svc_xprt.c index 493e70b72b71..4bda09d7e1a4 100644 --- a/net/sunrpc/svc_xprt.c +++ b/net/sunrpc/svc_xprt.c @@ -922,17 +922,19 @@ void svc_close_xprt(struct svc_xprt *xprt) } EXPORT_SYMBOL_GPL(svc_close_xprt); -static void svc_close_list(struct list_head *xprt_list) +static void svc_close_list(struct list_head *xprt_list, struct net *net) { struct svc_xprt *xprt; list_for_each_entry(xprt, xprt_list, xpt_list) { + if (xprt->xpt_net != net) + continue; set_bit(XPT_CLOSE, &xprt->xpt_flags); set_bit(XPT_BUSY, &xprt->xpt_flags); } } -static void svc_clear_pools(struct svc_serv *serv) +static void svc_clear_pools(struct svc_serv *serv, struct net *net) { struct svc_pool *pool; struct svc_xprt *xprt; @@ -944,36 +946,41 @@ static void svc_clear_pools(struct svc_serv *serv) spin_lock_bh(&pool->sp_lock); list_for_each_entry_safe(xprt, tmp, &pool->sp_sockets, xpt_ready) { + if (xprt->xpt_net != net) + continue; list_del_init(&xprt->xpt_ready); } spin_unlock_bh(&pool->sp_lock); } } -static void svc_clear_list(struct list_head *xprt_list) +static void svc_clear_list(struct list_head *xprt_list, struct net *net) { struct svc_xprt *xprt; struct svc_xprt *tmp; list_for_each_entry_safe(xprt, tmp, xprt_list, xpt_list) { + if (xprt->xpt_net != net) + continue; svc_delete_xprt(xprt); } - BUG_ON(!list_empty(xprt_list)); + list_for_each_entry(xprt, xprt_list, xpt_list) + BUG_ON(xprt->xpt_net == net); } -void svc_close_all(struct svc_serv *serv) +void svc_close_net(struct svc_serv *serv, struct net *net) { - svc_close_list(&serv->sv_tempsocks); - svc_close_list(&serv->sv_permsocks); + svc_close_list(&serv->sv_tempsocks, net); + svc_close_list(&serv->sv_permsocks, net); - svc_clear_pools(serv); + svc_clear_pools(serv, net); /* * At this point the sp_sockets lists will stay empty, since * svc_enqueue will not add new entries without taking the * sp_lock and checking XPT_BUSY. */ - svc_clear_list(&serv->sv_tempsocks); - svc_clear_list(&serv->sv_permsocks); + svc_clear_list(&serv->sv_tempsocks, net); + svc_clear_list(&serv->sv_permsocks, net); } /* -- cgit v1.2.3 From bb2224df5ffe4f864f5b696199b17db1ce77bc0a Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Tue, 31 Jan 2012 15:08:05 +0400 Subject: Lockd: per-net up and down routines introduced This patch introduces per-net Lockd initialization and destruction routines. The logic is the same as in global Lockd up and down routines. Probably the solution is not the best one. But at least it looks clear. So per-net "up" routine are called only in case of lockd is running already. If per-net resources are not allocated yet, then service is being registered with local portmapper and lockd sockets created. Per-net "down" routine is called on every lockd_down() call in case of global users counter is not zero. Signed-off-by: Stanislav Kinsbursky Signed-off-by: Trond Myklebust --- fs/lockd/svc.c | 47 ++++++++++++++++++++++++++++++++++++++++++++-- include/linux/sunrpc/svc.h | 2 ++ net/sunrpc/svc.c | 3 ++- 3 files changed, 49 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c index 73c9ebf09301..90dec426bfd8 100644 --- a/fs/lockd/svc.c +++ b/fs/lockd/svc.c @@ -251,6 +251,45 @@ out_err: return err; } +static int lockd_up_net(struct net *net) +{ + struct lockd_net *ln = net_generic(net, lockd_net_id); + struct svc_serv *serv = nlmsvc_rqst->rq_server; + int error; + + if (ln->nlmsvc_users) + return 0; + + error = svc_rpcb_setup(serv, net); + if (error) + goto err_rpcb; + + error = make_socks(serv, net); + if (error < 0) + goto err_socks; + return 0; + +err_socks: + svc_rpcb_cleanup(serv, net); +err_rpcb: + return error; +} + +static void lockd_down_net(struct net *net) +{ + struct lockd_net *ln = net_generic(net, lockd_net_id); + struct svc_serv *serv = nlmsvc_rqst->rq_server; + + if (ln->nlmsvc_users) { + if (--ln->nlmsvc_users == 0) + svc_shutdown_net(serv, net); + } else { + printk(KERN_ERR "lockd_down_net: no users! task=%p, net=%p\n", + nlmsvc_task, net); + BUG(); + } +} + /* * Bring up the lockd process if it's not already up. */ @@ -264,8 +303,10 @@ int lockd_up(void) /* * Check whether we're already up and running. */ - if (nlmsvc_rqst) + if (nlmsvc_rqst) { + error = lockd_up_net(net); goto out; + } /* * Sanity check: if there's no pid, @@ -339,8 +380,10 @@ lockd_down(void) { mutex_lock(&nlmsvc_mutex); if (nlmsvc_users) { - if (--nlmsvc_users) + if (--nlmsvc_users) { + lockd_down_net(current->nsproxy->net_ns); goto out; + } } else { printk(KERN_ERR "lockd_down: no users! task=%p\n", nlmsvc_task); diff --git a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h index 7b65495aa4ef..51b29ac45a8e 100644 --- a/include/linux/sunrpc/svc.h +++ b/include/linux/sunrpc/svc.h @@ -414,6 +414,7 @@ struct svc_procedure { /* * Function prototypes. */ +int svc_rpcb_setup(struct svc_serv *serv, struct net *net); void svc_rpcb_cleanup(struct svc_serv *serv, struct net *net); struct svc_serv *svc_create(struct svc_program *, unsigned int, void (*shutdown)(struct svc_serv *, struct net *net)); @@ -426,6 +427,7 @@ struct svc_serv * svc_create_pooled(struct svc_program *, unsigned int, int svc_set_num_threads(struct svc_serv *, struct svc_pool *, int); int svc_pool_stats_open(struct svc_serv *serv, struct file *file); void svc_destroy(struct svc_serv *); +void svc_shutdown_net(struct svc_serv *, struct net *); int svc_process(struct svc_rqst *); int bc_svc_process(struct svc_serv *, struct rpc_rqst *, struct svc_rqst *); diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index 78abac48985b..4153846984ac 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -369,7 +369,7 @@ svc_pool_for_cpu(struct svc_serv *serv, int cpu) return &serv->sv_pools[pidx % serv->sv_nrpools]; } -static int svc_rpcb_setup(struct svc_serv *serv, struct net *net) +int svc_rpcb_setup(struct svc_serv *serv, struct net *net) { int err; @@ -381,6 +381,7 @@ static int svc_rpcb_setup(struct svc_serv *serv, struct net *net) svc_unregister(serv, net); return 0; } +EXPORT_SYMBOL_GPL(svc_rpcb_setup); void svc_rpcb_cleanup(struct svc_serv *serv, struct net *net) { -- cgit v1.2.3 From 66697bfd6aec0a9ca9331c1aa544ac20324a7561 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Tue, 31 Jan 2012 15:08:13 +0400 Subject: LockD: make nlm hosts network namespace aware This object depends on RPC client, and thus on network namespace. So let's make it's allocation and lookup in network namespace context. Signed-off-by: Stanislav Kinsbursky Signed-off-by: Trond Myklebust --- fs/lockd/clntlock.c | 3 ++- fs/lockd/host.c | 16 ++++++++++++++-- fs/nfs/client.c | 1 + include/linux/lockd/bind.h | 1 + include/linux/lockd/lockd.h | 4 +++- 5 files changed, 21 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/fs/lockd/clntlock.c b/fs/lockd/clntlock.c index 8d4ea8351e3d..ba1dc2eebd1e 100644 --- a/fs/lockd/clntlock.c +++ b/fs/lockd/clntlock.c @@ -62,7 +62,8 @@ struct nlm_host *nlmclnt_init(const struct nlmclnt_initdata *nlm_init) host = nlmclnt_lookup_host(nlm_init->address, nlm_init->addrlen, nlm_init->protocol, nlm_version, - nlm_init->hostname, nlm_init->noresvport); + nlm_init->hostname, nlm_init->noresvport, + nlm_init->net); if (host == NULL) { lockd_down(); return ERR_PTR(-ENOLCK); diff --git a/fs/lockd/host.c b/fs/lockd/host.c index 6f29836ec0cb..9ebd91dc42c3 100644 --- a/fs/lockd/host.c +++ b/fs/lockd/host.c @@ -17,6 +17,8 @@ #include #include +#include + #include #define NLMDBG_FACILITY NLMDBG_HOSTCACHE @@ -54,6 +56,7 @@ struct nlm_lookup_host_info { const char *hostname; /* remote's hostname */ const size_t hostname_len; /* it's length */ const int noresvport; /* use non-priv port */ + struct net *net; /* network namespace to bind */ }; /* @@ -155,6 +158,7 @@ static struct nlm_host *nlm_alloc_host(struct nlm_lookup_host_info *ni, INIT_LIST_HEAD(&host->h_reclaim); host->h_nsmhandle = nsm; host->h_addrbuf = nsm->sm_addrbuf; + host->net = ni->net; out: return host; @@ -206,7 +210,8 @@ struct nlm_host *nlmclnt_lookup_host(const struct sockaddr *sap, const unsigned short protocol, const u32 version, const char *hostname, - int noresvport) + int noresvport, + struct net *net) { struct nlm_lookup_host_info ni = { .server = 0, @@ -217,6 +222,7 @@ struct nlm_host *nlmclnt_lookup_host(const struct sockaddr *sap, .hostname = hostname, .hostname_len = strlen(hostname), .noresvport = noresvport, + .net = net, }; struct hlist_head *chain; struct hlist_node *pos; @@ -231,6 +237,8 @@ struct nlm_host *nlmclnt_lookup_host(const struct sockaddr *sap, chain = &nlm_client_hosts[nlm_hash_address(sap)]; hlist_for_each_entry(host, pos, chain, h_hash) { + if (host->net != net) + continue; if (!rpc_cmp_addr(nlm_addr(host), sap)) continue; @@ -318,6 +326,7 @@ struct nlm_host *nlmsvc_lookup_host(const struct svc_rqst *rqstp, struct nsm_handle *nsm = NULL; struct sockaddr *src_sap = svc_daddr(rqstp); size_t src_len = rqstp->rq_daddrlen; + struct net *net = rqstp->rq_xprt->xpt_net; struct nlm_lookup_host_info ni = { .server = 1, .sap = svc_addr(rqstp), @@ -326,6 +335,7 @@ struct nlm_host *nlmsvc_lookup_host(const struct svc_rqst *rqstp, .version = rqstp->rq_vers, .hostname = hostname, .hostname_len = hostname_len, + .net = net, }; dprintk("lockd: %s(host='%*s', vers=%u, proto=%s)\n", __func__, @@ -339,6 +349,8 @@ struct nlm_host *nlmsvc_lookup_host(const struct svc_rqst *rqstp, chain = &nlm_server_hosts[nlm_hash_address(ni.sap)]; hlist_for_each_entry(host, pos, chain, h_hash) { + if (host->net != net) + continue; if (!rpc_cmp_addr(nlm_addr(host), ni.sap)) continue; @@ -431,7 +443,7 @@ nlm_bind_host(struct nlm_host *host) .to_retries = 5U, }; struct rpc_create_args args = { - .net = &init_net, + .net = host->net, .protocol = host->h_proto, .address = nlm_addr(host), .addrsize = host->h_addrlen, diff --git a/fs/nfs/client.c b/fs/nfs/client.c index 2328dcbf6c0b..1a5cd49dff80 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -707,6 +707,7 @@ static int nfs_start_lockd(struct nfs_server *server) .nfs_version = clp->rpc_ops->version, .noresvport = server->flags & NFS_MOUNT_NORESVPORT ? 1 : 0, + .net = clp->net, }; if (nlm_init.nfs_version > 3) diff --git a/include/linux/lockd/bind.h b/include/linux/lockd/bind.h index fbc48f898521..11a966e5f829 100644 --- a/include/linux/lockd/bind.h +++ b/include/linux/lockd/bind.h @@ -42,6 +42,7 @@ struct nlmclnt_initdata { unsigned short protocol; u32 nfs_version; int noresvport; + struct net *net; }; /* diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h index 8949167a148d..94b3d13be426 100644 --- a/include/linux/lockd/lockd.h +++ b/include/linux/lockd/lockd.h @@ -67,6 +67,7 @@ struct nlm_host { struct list_head h_reclaim; /* Locks in RECLAIM state */ struct nsm_handle *h_nsmhandle; /* NSM status handle */ char *h_addrbuf; /* address eyecatcher */ + struct net *net; /* host net */ }; /* @@ -222,7 +223,8 @@ struct nlm_host *nlmclnt_lookup_host(const struct sockaddr *sap, const unsigned short protocol, const u32 version, const char *hostname, - int noresvport); + int noresvport, + struct net *net); void nlmclnt_release_host(struct nlm_host *); struct nlm_host *nlmsvc_lookup_host(const struct svc_rqst *rqstp, const char *hostname, -- cgit v1.2.3 From 3b64739fb928c34b13db6b5adcb0d3efb19e78be Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Tue, 31 Jan 2012 15:08:29 +0400 Subject: Lockd: shutdown NLM hosts in network namespace context Lockd now managed in network namespace context. And this patch introduces network namespace related NLM hosts shutdown in case of releasing per-net Lockd resources. Signed-off-by: Stanislav Kinsbursky Signed-off-by: Trond Myklebust --- fs/lockd/host.c | 26 +++++++++++++++++++------- fs/lockd/svc.c | 4 +++- include/linux/lockd/lockd.h | 1 + 3 files changed, 23 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/fs/lockd/host.c b/fs/lockd/host.c index 9ebd91dc42c3..eb75ca7c2d6e 100644 --- a/fs/lockd/host.c +++ b/fs/lockd/host.c @@ -565,12 +565,8 @@ void nlm_host_rebooted(const struct nlm_reboot *info) nsm_release(nsm); } -/* - * Shut down the hosts module. - * Note that this routine is called only at server shutdown time. - */ void -nlm_shutdown_hosts(void) +nlm_shutdown_hosts_net(struct net *net) { struct hlist_head *chain; struct hlist_node *pos; @@ -582,6 +578,8 @@ nlm_shutdown_hosts(void) /* First, make all hosts eligible for gc */ dprintk("lockd: nuking all hosts...\n"); for_each_host(host, pos, chain, nlm_server_hosts) { + if (net && host->net != net) + continue; host->h_expires = jiffies - 1; if (host->h_rpcclnt) { rpc_shutdown_client(host->h_rpcclnt); @@ -592,15 +590,29 @@ nlm_shutdown_hosts(void) /* Then, perform a garbage collection pass */ nlm_gc_hosts(); mutex_unlock(&nlm_host_mutex); +} + +/* + * Shut down the hosts module. + * Note that this routine is called only at server shutdown time. + */ +void +nlm_shutdown_hosts(void) +{ + struct hlist_head *chain; + struct hlist_node *pos; + struct nlm_host *host; + + nlm_shutdown_hosts_net(NULL); /* complain if any hosts are left */ if (nrhosts != 0) { printk(KERN_WARNING "lockd: couldn't shutdown host module!\n"); dprintk("lockd: %lu hosts left:\n", nrhosts); for_each_host(host, pos, chain, nlm_server_hosts) { - dprintk(" %s (cnt %d use %d exp %ld)\n", + dprintk(" %s (cnt %d use %d exp %ld net %p)\n", host->h_name, atomic_read(&host->h_count), - host->h_inuse, host->h_expires); + host->h_inuse, host->h_expires, host->net); } } } diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c index 90dec426bfd8..2774e1013b34 100644 --- a/fs/lockd/svc.c +++ b/fs/lockd/svc.c @@ -281,8 +281,10 @@ static void lockd_down_net(struct net *net) struct svc_serv *serv = nlmsvc_rqst->rq_server; if (ln->nlmsvc_users) { - if (--ln->nlmsvc_users == 0) + if (--ln->nlmsvc_users == 0) { + nlm_shutdown_hosts_net(net); svc_shutdown_net(serv, net); + } } else { printk(KERN_ERR "lockd_down_net: no users! task=%p, net=%p\n", nlmsvc_task, net); diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h index 94b3d13be426..f04ce6ac6d04 100644 --- a/include/linux/lockd/lockd.h +++ b/include/linux/lockd/lockd.h @@ -234,6 +234,7 @@ struct rpc_clnt * nlm_bind_host(struct nlm_host *); void nlm_rebind_host(struct nlm_host *); struct nlm_host * nlm_get_host(struct nlm_host *); void nlm_shutdown_hosts(void); +void nlm_shutdown_hosts_net(struct net *net); void nlm_host_rebooted(const struct nlm_reboot *); /* -- cgit v1.2.3 From 2f09c24216cd789653eb8efbf8be88409eb8d581 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Wed, 8 Feb 2012 22:01:15 -0500 Subject: SUNRPC: Ensure that we can trace waitqueues when !defined(CONFIG_SYSCTL) The tracepoint code relies on the queue->name being defined in order to be able to display the name of the waitqueue on which an RPC task is sleeping. Reported-by: Randy Dunlap Reported-by: Steven Rostedt Signed-off-by: Trond Myklebust Acked-by: Steven Rostedt Acked-by: Randy Dunlap --- include/linux/sunrpc/debug.h | 3 +++ include/linux/sunrpc/sched.h | 15 +++++++++++++-- net/sunrpc/sched.c | 4 +--- 3 files changed, 17 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sunrpc/debug.h b/include/linux/sunrpc/debug.h index c2786f20016f..2a11eb278f64 100644 --- a/include/linux/sunrpc/debug.h +++ b/include/linux/sunrpc/debug.h @@ -34,6 +34,9 @@ #ifdef CONFIG_SYSCTL #define RPC_DEBUG #endif +#ifdef CONFIG_TRACEPOINTS +#define RPC_TRACEPOINTS +#endif /* #define RPC_PROFILE */ /* diff --git a/include/linux/sunrpc/sched.h b/include/linux/sunrpc/sched.h index f7b2df5252b0..22dfc24013b6 100644 --- a/include/linux/sunrpc/sched.h +++ b/include/linux/sunrpc/sched.h @@ -195,7 +195,7 @@ struct rpc_wait_queue { unsigned char nr; /* # tasks remaining for cookie */ unsigned short qlen; /* total # tasks waiting in queue */ struct rpc_timer timer_list; -#ifdef RPC_DEBUG +#if defined(RPC_DEBUG) || defined(RPC_TRACEPOINTS) const char * name; #endif }; @@ -270,11 +270,22 @@ static inline int rpc_task_has_priority(struct rpc_task *task, unsigned char pri return (task->tk_priority + RPC_PRIORITY_LOW == prio); } -#ifdef RPC_DEBUG +#if defined(RPC_DEBUG) || defined (RPC_TRACEPOINTS) static inline const char * rpc_qname(const struct rpc_wait_queue *q) { return ((q && q->name) ? q->name : "unknown"); } + +static inline void rpc_assign_waitqueue_name(struct rpc_wait_queue *q, + const char *name) +{ + q->name = name; +} +#else +static inline void rpc_assign_waitqueue_name(struct rpc_wait_queue *q, + const char *name) +{ +} #endif #endif /* _LINUX_SUNRPC_SCHED_H_ */ diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c index d79c63df49b8..1c570a81096a 100644 --- a/net/sunrpc/sched.c +++ b/net/sunrpc/sched.c @@ -208,9 +208,7 @@ static void __rpc_init_priority_wait_queue(struct rpc_wait_queue *queue, const c queue->qlen = 0; setup_timer(&queue->timer_list.timer, __rpc_queue_timer_fn, (unsigned long)queue); INIT_LIST_HEAD(&queue->timer_list.list); -#ifdef RPC_DEBUG - queue->name = qname; -#endif + rpc_assign_waitqueue_name(queue, qname); } void rpc_init_priority_wait_queue(struct rpc_wait_queue *queue, const char *qname) -- cgit v1.2.3 From 15a4520621824a3c2eb2de2d1f3984bc1663d3c8 Mon Sep 17 00:00:00 2001 From: Andy Adamson Date: Tue, 14 Feb 2012 16:19:18 -0500 Subject: SUNRPC: add sending,pending queue and max slot to xprt stats With static RPC slots, the xprt backlog queue stats were useful in showing when the transport (TCP) was starved by lack of RPC slots. The new dynamic RPC slot code, commit d9ba131d8f58c0d2ff5029e7002ab43f913b36f9, always provides an RPC slot and so only uses the xprt backlog queue when the tcp_max_slot_table_entries value has been hit or when an allocation error occurs. All requests are now placed on the xprt sending or pending queue which need to be monitored for debugging. The max_slot stat shows the maximum number of dynamic RPC slots reached which is useful when debugging performance issues. Add the new fields at the end of the mountstats xprt stanza so that mountstats outputs the previous correct values and ignores the new fields. Bump NFS_IOSTATS_VERS. Signed-off-by: Andy Adamson Signed-off-by: Trond Myklebust --- include/linux/nfs_iostat.h | 2 +- include/linux/sunrpc/xprt.h | 7 +++++-- net/sunrpc/xprt.c | 7 ++++++- net/sunrpc/xprtsock.c | 23 +++++++++++++++++------ 4 files changed, 29 insertions(+), 10 deletions(-) (limited to 'include/linux') diff --git a/include/linux/nfs_iostat.h b/include/linux/nfs_iostat.h index 8866bb3502ee..9dcbbe9a51fb 100644 --- a/include/linux/nfs_iostat.h +++ b/include/linux/nfs_iostat.h @@ -21,7 +21,7 @@ #ifndef _LINUX_NFS_IOSTAT #define _LINUX_NFS_IOSTAT -#define NFS_IOSTAT_VERS "1.0" +#define NFS_IOSTAT_VERS "1.1" /* * NFS byte counters diff --git a/include/linux/sunrpc/xprt.h b/include/linux/sunrpc/xprt.h index b033f366d5f6..ea712f97f4a1 100644 --- a/include/linux/sunrpc/xprt.h +++ b/include/linux/sunrpc/xprt.h @@ -219,10 +219,13 @@ struct rpc_xprt { connect_time, /* jiffies waiting for connect */ sends, /* how many complete requests */ recvs, /* how many complete requests */ - bad_xids; /* lookup_rqst didn't find XID */ + bad_xids, /* lookup_rqst didn't find XID */ + max_slots; /* max rpc_slots used */ unsigned long long req_u, /* average requests on the wire */ - bklog_u; /* backlog queue utilization */ + bklog_u, /* backlog queue utilization */ + sending_u, /* send q utilization */ + pending_u; /* pend q utilization */ } stat; struct net *xprt_net; diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c index efe5495ecf65..739df8a11382 100644 --- a/net/sunrpc/xprt.c +++ b/net/sunrpc/xprt.c @@ -885,7 +885,7 @@ void xprt_transmit(struct rpc_task *task) { struct rpc_rqst *req = task->tk_rqstp; struct rpc_xprt *xprt = req->rq_xprt; - int status; + int status, numreqs; dprintk("RPC: %5u xprt_transmit(%u)\n", task->tk_pid, req->rq_slen); @@ -922,9 +922,14 @@ void xprt_transmit(struct rpc_task *task) xprt->ops->set_retrans_timeout(task); + numreqs = atomic_read(&xprt->num_reqs); + if (numreqs > xprt->stat.max_slots) + xprt->stat.max_slots = numreqs; xprt->stat.sends++; xprt->stat.req_u += xprt->stat.sends - xprt->stat.recvs; xprt->stat.bklog_u += xprt->backlog.qlen; + xprt->stat.sending_u += xprt->sending.qlen; + xprt->stat.pending_u += xprt->pending.qlen; /* Don't race with disconnect */ if (!xprt_connected(xprt)) diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c index 55472c48825e..4c8281d29e2b 100644 --- a/net/sunrpc/xprtsock.c +++ b/net/sunrpc/xprtsock.c @@ -2227,7 +2227,7 @@ static void xs_local_print_stats(struct rpc_xprt *xprt, struct seq_file *seq) idle_time = (long)(jiffies - xprt->last_used) / HZ; seq_printf(seq, "\txprt:\tlocal %lu %lu %lu %ld %lu %lu %lu " - "%llu %llu\n", + "%llu %llu %lu %llu %llu\n", xprt->stat.bind_count, xprt->stat.connect_count, xprt->stat.connect_time, @@ -2236,7 +2236,10 @@ static void xs_local_print_stats(struct rpc_xprt *xprt, struct seq_file *seq) xprt->stat.recvs, xprt->stat.bad_xids, xprt->stat.req_u, - xprt->stat.bklog_u); + xprt->stat.bklog_u, + xprt->stat.max_slots, + xprt->stat.sending_u, + xprt->stat.pending_u); } /** @@ -2249,14 +2252,18 @@ static void xs_udp_print_stats(struct rpc_xprt *xprt, struct seq_file *seq) { struct sock_xprt *transport = container_of(xprt, struct sock_xprt, xprt); - seq_printf(seq, "\txprt:\tudp %u %lu %lu %lu %lu %Lu %Lu\n", + seq_printf(seq, "\txprt:\tudp %u %lu %lu %lu %lu %llu %llu " + "%lu %llu %llu\n", transport->srcport, xprt->stat.bind_count, xprt->stat.sends, xprt->stat.recvs, xprt->stat.bad_xids, xprt->stat.req_u, - xprt->stat.bklog_u); + xprt->stat.bklog_u, + xprt->stat.max_slots, + xprt->stat.sending_u, + xprt->stat.pending_u); } /** @@ -2273,7 +2280,8 @@ static void xs_tcp_print_stats(struct rpc_xprt *xprt, struct seq_file *seq) if (xprt_connected(xprt)) idle_time = (long)(jiffies - xprt->last_used) / HZ; - seq_printf(seq, "\txprt:\ttcp %u %lu %lu %lu %ld %lu %lu %lu %Lu %Lu\n", + seq_printf(seq, "\txprt:\ttcp %u %lu %lu %lu %ld %lu %lu %lu " + "%llu %llu %lu %llu %llu\n", transport->srcport, xprt->stat.bind_count, xprt->stat.connect_count, @@ -2283,7 +2291,10 @@ static void xs_tcp_print_stats(struct rpc_xprt *xprt, struct seq_file *seq) xprt->stat.recvs, xprt->stat.bad_xids, xprt->stat.req_u, - xprt->stat.bklog_u); + xprt->stat.bklog_u, + xprt->stat.max_slots, + xprt->stat.sending_u, + xprt->stat.pending_u); } /* -- cgit v1.2.3 From dbb9c2a22d32492544765e798386daa2d9da27d2 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Wed, 15 Feb 2012 16:35:08 -0500 Subject: SUNRPC: Use KERN_DEFAULT for debugging printk's Our dprintk() debugging facility doesn't specify any verbosity level for it's printk() calls, but it should. The default verbosity for printk's is KERN_DEFAULT. You might argue that these are debugging printk's and thus the verbosity should be KERN_DEBUG. That would mean that to see NFS and SUNRPC debugging output an admin would also have to boost the syslog verbosity, which would be insufferably noisy. Signed-off-by: Chuck Lever Signed-off-by: Trond Myklebust --- include/linux/sunrpc/debug.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/sunrpc/debug.h b/include/linux/sunrpc/debug.h index 2a11eb278f64..b506936f4ce6 100644 --- a/include/linux/sunrpc/debug.h +++ b/include/linux/sunrpc/debug.h @@ -54,7 +54,11 @@ extern unsigned int nlm_debug; #undef ifdebug #ifdef RPC_DEBUG # define ifdebug(fac) if (unlikely(rpc_debug & RPCDBG_##fac)) -# define dfprintk(fac, args...) do { ifdebug(fac) printk(args); } while(0) +# define dfprintk(fac, args...) \ + do { \ + ifdebug(fac) \ + printk(KERN_DEFAULT args); \ + } while (0) # define RPC_IFDEBUG(x) x #else # define ifdebug(fac) if (0) -- cgit v1.2.3 From 0a702195234eb77c4097148285cccf7f095de9cf Mon Sep 17 00:00:00 2001 From: Weston Andros Adamson Date: Fri, 17 Feb 2012 13:15:24 -0500 Subject: NFS: include filelayout DS rpc stats in mountstats Include RPC statistics from all data servers in /proc/self/mountstats for pNFS filelayout mounts. Signed-off-by: Weston Andros Adamson Signed-off-by: Trond Myklebust --- fs/nfs/nfs4filelayout.c | 19 +++++++++++++++++++ include/linux/sunrpc/metrics.h | 6 ++++-- include/linux/sunrpc/sched.h | 1 + net/sunrpc/stats.c | 8 ++++---- net/sunrpc/xprt.c | 5 ++++- 5 files changed, 32 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/fs/nfs/nfs4filelayout.c b/fs/nfs/nfs4filelayout.c index 79be7acc9bae..47e8f3435d38 100644 --- a/fs/nfs/nfs4filelayout.c +++ b/fs/nfs/nfs4filelayout.c @@ -33,6 +33,8 @@ #include #include +#include + #include "internal.h" #include "nfs4filelayout.h" @@ -189,6 +191,13 @@ static void filelayout_read_call_done(struct rpc_task *task, void *data) rdata->mds_ops->rpc_call_done(task, data); } +static void filelayout_read_count_stats(struct rpc_task *task, void *data) +{ + struct nfs_read_data *rdata = (struct nfs_read_data *)data; + + rpc_count_iostats(task, NFS_SERVER(rdata->inode)->client->cl_metrics); +} + static void filelayout_read_release(void *data) { struct nfs_read_data *rdata = (struct nfs_read_data *)data; @@ -268,6 +277,13 @@ static void filelayout_write_call_done(struct rpc_task *task, void *data) wdata->mds_ops->rpc_call_done(task, data); } +static void filelayout_write_count_stats(struct rpc_task *task, void *data) +{ + struct nfs_write_data *wdata = (struct nfs_write_data *)data; + + rpc_count_iostats(task, NFS_SERVER(wdata->inode)->client->cl_metrics); +} + static void filelayout_write_release(void *data) { struct nfs_write_data *wdata = (struct nfs_write_data *)data; @@ -288,18 +304,21 @@ static void filelayout_commit_release(void *data) struct rpc_call_ops filelayout_read_call_ops = { .rpc_call_prepare = filelayout_read_prepare, .rpc_call_done = filelayout_read_call_done, + .rpc_count_stats = filelayout_read_count_stats, .rpc_release = filelayout_read_release, }; struct rpc_call_ops filelayout_write_call_ops = { .rpc_call_prepare = filelayout_write_prepare, .rpc_call_done = filelayout_write_call_done, + .rpc_count_stats = filelayout_write_count_stats, .rpc_release = filelayout_write_release, }; struct rpc_call_ops filelayout_commit_call_ops = { .rpc_call_prepare = filelayout_write_prepare, .rpc_call_done = filelayout_write_call_done, + .rpc_count_stats = filelayout_write_count_stats, .rpc_release = filelayout_commit_release, }; diff --git a/include/linux/sunrpc/metrics.h b/include/linux/sunrpc/metrics.h index b6edbc0ea83d..1565bbe86d51 100644 --- a/include/linux/sunrpc/metrics.h +++ b/include/linux/sunrpc/metrics.h @@ -74,14 +74,16 @@ struct rpc_clnt; #ifdef CONFIG_PROC_FS struct rpc_iostats * rpc_alloc_iostats(struct rpc_clnt *); -void rpc_count_iostats(struct rpc_task *); +void rpc_count_iostats(const struct rpc_task *, + struct rpc_iostats *); void rpc_print_iostats(struct seq_file *, struct rpc_clnt *); void rpc_free_iostats(struct rpc_iostats *); #else /* CONFIG_PROC_FS */ static inline struct rpc_iostats *rpc_alloc_iostats(struct rpc_clnt *clnt) { return NULL; } -static inline void rpc_count_iostats(struct rpc_task *task) {} +static inline void rpc_count_iostats(const struct rpc_task *task, + struct rpc_iostats *stats) {} static inline void rpc_print_iostats(struct seq_file *seq, struct rpc_clnt *clnt) {} static inline void rpc_free_iostats(struct rpc_iostats *stats) {} diff --git a/include/linux/sunrpc/sched.h b/include/linux/sunrpc/sched.h index 22dfc24013b6..dc0c3cc3ada3 100644 --- a/include/linux/sunrpc/sched.h +++ b/include/linux/sunrpc/sched.h @@ -103,6 +103,7 @@ typedef void (*rpc_action)(struct rpc_task *); struct rpc_call_ops { void (*rpc_call_prepare)(struct rpc_task *, void *); void (*rpc_call_done)(struct rpc_task *, void *); + void (*rpc_count_stats)(struct rpc_task *, void *); void (*rpc_release)(void *); }; diff --git a/net/sunrpc/stats.c b/net/sunrpc/stats.c index 3c4f6888c891..1eb3304bc105 100644 --- a/net/sunrpc/stats.c +++ b/net/sunrpc/stats.c @@ -133,20 +133,19 @@ EXPORT_SYMBOL_GPL(rpc_free_iostats); /** * rpc_count_iostats - tally up per-task stats * @task: completed rpc_task + * @stats: array of stat structures * * Relies on the caller for serialization. */ -void rpc_count_iostats(struct rpc_task *task) +void rpc_count_iostats(const struct rpc_task *task, struct rpc_iostats *stats) { struct rpc_rqst *req = task->tk_rqstp; - struct rpc_iostats *stats; struct rpc_iostats *op_metrics; ktime_t delta; - if (!task->tk_client || !task->tk_client->cl_metrics || !req) + if (!stats || !req) return; - stats = task->tk_client->cl_metrics; op_metrics = &stats[task->tk_msg.rpc_proc->p_statidx]; op_metrics->om_ops++; @@ -164,6 +163,7 @@ void rpc_count_iostats(struct rpc_task *task) delta = ktime_sub(ktime_get(), task->tk_start); op_metrics->om_execute = ktime_add(op_metrics->om_execute, delta); } +EXPORT_SYMBOL_GPL(rpc_count_iostats); static void _print_name(struct seq_file *seq, unsigned int op, struct rpc_procinfo *procs) diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c index 739df8a11382..32e37945a840 100644 --- a/net/sunrpc/xprt.c +++ b/net/sunrpc/xprt.c @@ -1137,7 +1137,10 @@ void xprt_release(struct rpc_task *task) return; xprt = req->rq_xprt; - rpc_count_iostats(task); + if (task->tk_ops->rpc_count_stats != NULL) + task->tk_ops->rpc_count_stats(task, task->tk_calldata); + else if (task->tk_client) + rpc_count_iostats(task, task->tk_client->cl_metrics); spin_lock_bh(&xprt->transport_lock); xprt->ops->release_xprt(xprt, task); if (xprt->ops->release_request) -- cgit v1.2.3 From 9cde5fcd435fd929db7b0b486efb435cdb1ddca4 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 17 Feb 2012 14:49:51 -0800 Subject: regmap: Add stub regmap calls as a build crutch for infrastructure users Make life easier for subsystems which build infrastructure on top of the regmap API by providing stub definitions for most of the API so that users can at least build even if the regmap API is not enabled without having to add ifdefs if they don't want to. These stubs are not expected to be useful and should never actually get called, they just exist so that we can link so there are WARN_ONCE()s in the stubs. Signed-off-by: Mark Brown --- include/linux/regmap.h | 106 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) (limited to 'include/linux') diff --git a/include/linux/regmap.h b/include/linux/regmap.h index a6ed6e6e27ac..42c69e75b131 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -16,6 +16,8 @@ #include #include +#ifdef CONFIG_REGMAP + struct module; struct i2c_client; struct spi_device; @@ -199,4 +201,108 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, void regmap_del_irq_chip(int irq, struct regmap_irq_chip_data *data); int regmap_irq_chip_get_base(struct regmap_irq_chip_data *data); +#else + +/* + * These stubs should only ever be called by generic code which has + * regmap based facilities, if they ever get called at runtime + * something is going wrong and something probably needs to select + * REGMAP. + */ + +static inline int regmap_write(struct regmap *map, unsigned int reg, + unsigned int val) +{ + WARN_ONCE(1, "regmap API is disabled"); + return -EINVAL; +} + +static inline int regmap_raw_write(struct regmap *map, unsigned int reg, + const void *val, size_t val_len) +{ + WARN_ONCE(1, "regmap API is disabled"); + return -EINVAL; +} + +static inline int regmap_bulk_write(struct regmap *map, unsigned int reg, + const void *val, size_t val_count) +{ + WARN_ONCE(1, "regmap API is disabled"); + return -EINVAL; +} + +static inline int regmap_read(struct regmap *map, unsigned int reg, + unsigned int *val) +{ + WARN_ONCE(1, "regmap API is disabled"); + return -EINVAL; +} + +static inline int regmap_raw_read(struct regmap *map, unsigned int reg, + void *val, size_t val_len) +{ + WARN_ONCE(1, "regmap API is disabled"); + return -EINVAL; +} + +static inline int regmap_bulk_read(struct regmap *map, unsigned int reg, + void *val, size_t val_count) +{ + WARN_ONCE(1, "regmap API is disabled"); + return -EINVAL; +} + +static inline int regmap_update_bits(struct regmap *map, unsigned int reg, + unsigned int mask, unsigned int val) +{ + WARN_ONCE(1, "regmap API is disabled"); + return -EINVAL; +} + +static inline int regmap_update_bits_check(struct regmap *map, + unsigned int reg, + unsigned int mask, unsigned int val, + bool *change) +{ + WARN_ONCE(1, "regmap API is disabled"); + return -EINVAL; +} + +static inline int regmap_get_val_bytes(struct regmap *map) +{ + WARN_ONCE(1, "regmap API is disabled"); + return -EINVAL; +} + +static inline int regcache_sync(struct regmap *map) +{ + WARN_ONCE(1, "regmap API is disabled"); + return -EINVAL; +} + +static inline void regcache_cache_only(struct regmap *map, bool enable) +{ + WARN_ONCE(1, "regmap API is disabled"); +} + +static inline void regcache_cache_bypass(struct regmap *map, bool enable) +{ + WARN_ONCE(1, "regmap API is disabled"); +} + +static inline void regcache_mark_dirty(struct regmap *map) +{ + WARN_ONCE(1, "regmap API is disabled"); +} + +static inline int regmap_register_patch(struct regmap *map, + const struct reg_default *regs, + int num_regs) +{ + WARN_ONCE(1, "regmap API is disabled"); + return -EINVAL; +} + +#endif + #endif -- cgit v1.2.3 From abe06082d07fcb0673cb93338c1d6f037fdc375b Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 20 Jan 2012 22:13:52 +0000 Subject: MFD: mcp/ucb1x00: separate ucb1x00 driver data from the MCP data Patch taken from 5dd7bf59e0 (ARM: sa11x0: Implement autoloading of codec and codec pdata for mcp bus.) by Jochen Friedrich . This adds just the codec data part of the patch. Acked-by: Jochen Friedrich Signed-off-by: Russell King --- arch/arm/mach-sa1100/collie.c | 7 ++++++- arch/arm/mach-sa1100/include/mach/mcp.h | 2 +- arch/arm/mach-sa1100/simpad.c | 7 ++++++- drivers/mfd/mcp-core.c | 3 ++- drivers/mfd/mcp-sa11x0.c | 3 +-- drivers/mfd/ucb1x00-core.c | 7 ++++--- include/linux/mfd/mcp.h | 3 +-- include/linux/mfd/ucb1x00.h | 3 +++ 8 files changed, 24 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/arch/arm/mach-sa1100/collie.c b/arch/arm/mach-sa1100/collie.c index efa2bc132cbf..0e7359785159 100644 --- a/arch/arm/mach-sa1100/collie.c +++ b/arch/arm/mach-sa1100/collie.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -85,10 +86,14 @@ static struct scoop_pcmcia_config collie_pcmcia_config = { .num_devs = 1, }; +static struct ucb1x00_plat_data collie_ucb1x00_data = { + .gpio_base = COLLIE_TC35143_GPIO_BASE, +}; + static struct mcp_plat_data collie_mcp_data = { .mccr0 = MCCR0_ADM | MCCR0_ExtClk, .sclk_rate = 9216000, - .gpio_base = COLLIE_TC35143_GPIO_BASE, + .codec_pdata = &collie_ucb1x00_data, }; /* diff --git a/arch/arm/mach-sa1100/include/mach/mcp.h b/arch/arm/mach-sa1100/include/mach/mcp.h index ed1a331508a7..4b2860ae3828 100644 --- a/arch/arm/mach-sa1100/include/mach/mcp.h +++ b/arch/arm/mach-sa1100/include/mach/mcp.h @@ -16,7 +16,7 @@ struct mcp_plat_data { u32 mccr0; u32 mccr1; unsigned int sclk_rate; - int gpio_base; + void *codec_pdata; }; #endif diff --git a/arch/arm/mach-sa1100/simpad.c b/arch/arm/mach-sa1100/simpad.c index 3aa36ec21039..81506562ee26 100644 --- a/arch/arm/mach-sa1100/simpad.c +++ b/arch/arm/mach-sa1100/simpad.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -187,10 +188,14 @@ static struct resource simpad_flash_resources [] = { } }; +static struct ucb1x00_plat_data simpad_ucb1x00_data = { + .gpio_base = SIMPAD_UCB1X00_GPIO_BASE, +}; + static struct mcp_plat_data simpad_mcp_data = { .mccr0 = MCCR0_ADM, .sclk_rate = 11981000, - .gpio_base = SIMPAD_UCB1X00_GPIO_BASE, + .codec_pdata = &simpad_ucb1x00_data, }; diff --git a/drivers/mfd/mcp-core.c b/drivers/mfd/mcp-core.c index 280a4f8a7876..c409d6327140 100644 --- a/drivers/mfd/mcp-core.c +++ b/drivers/mfd/mcp-core.c @@ -217,8 +217,9 @@ struct mcp *mcp_host_alloc(struct device *parent, size_t size) } EXPORT_SYMBOL(mcp_host_alloc); -int mcp_host_add(struct mcp *mcp) +int mcp_host_add(struct mcp *mcp, void *pdata) { + mcp->attached_device.platform_data = pdata; dev_set_name(&mcp->attached_device, "mcp0"); return device_add(&mcp->attached_device); } diff --git a/drivers/mfd/mcp-sa11x0.c b/drivers/mfd/mcp-sa11x0.c index 420710b19f2d..960ebc790389 100644 --- a/drivers/mfd/mcp-sa11x0.c +++ b/drivers/mfd/mcp-sa11x0.c @@ -194,7 +194,6 @@ static int mcp_sa11x0_probe(struct platform_device *dev) mcp->owner = THIS_MODULE; mcp->ops = &mcp_sa11x0; mcp->sclk_rate = data->sclk_rate; - mcp->gpio_base = data->gpio_base; m = priv(mcp); m->mccr0 = data->mccr0 | 0x7f7f; @@ -229,7 +228,7 @@ static int mcp_sa11x0_probe(struct platform_device *dev) mcp->rw_timeout = (64 * 3 * 1000000 + mcp->sclk_rate - 1) / mcp->sclk_rate; - ret = mcp_host_add(mcp); + ret = mcp_host_add(mcp, data->codec_pdata); if (ret == 0) return 0; diff --git a/drivers/mfd/ucb1x00-core.c b/drivers/mfd/ucb1x00-core.c index f2fb4205467c..6825169b06fc 100644 --- a/drivers/mfd/ucb1x00-core.c +++ b/drivers/mfd/ucb1x00-core.c @@ -534,6 +534,7 @@ static int ucb1x00_probe(struct mcp *mcp) { struct ucb1x00 *ucb; struct ucb1x00_driver *drv; + struct ucb1x00_plat_data *pdata; unsigned int id; int ret = -ENODEV; int temp; @@ -551,7 +552,7 @@ static int ucb1x00_probe(struct mcp *mcp) if (!ucb) goto err_disable; - + pdata = mcp->attached_device.platform_data; ucb->dev.class = &ucb1x00_class; ucb->dev.parent = &mcp->attached_device; dev_set_name(&ucb->dev, "ucb1x00"); @@ -570,9 +571,9 @@ static int ucb1x00_probe(struct mcp *mcp) } ucb->gpio.base = -1; - if (mcp->gpio_base != 0) { + if (pdata && pdata->gpio_base) { ucb->gpio.label = dev_name(&ucb->dev); - ucb->gpio.base = mcp->gpio_base; + ucb->gpio.base = pdata->gpio_base; ucb->gpio.ngpio = 10; ucb->gpio.set = ucb1x00_gpio_set; ucb->gpio.get = ucb1x00_gpio_get; diff --git a/include/linux/mfd/mcp.h b/include/linux/mfd/mcp.h index dfe7e517ad9b..bfcdf6d3f1bf 100644 --- a/include/linux/mfd/mcp.h +++ b/include/linux/mfd/mcp.h @@ -20,7 +20,6 @@ struct mcp { unsigned int sclk_rate; unsigned int rw_timeout; struct device attached_device; - int gpio_base; }; struct mcp_ops { @@ -41,7 +40,7 @@ void mcp_disable(struct mcp *); #define mcp_get_sclk_rate(mcp) ((mcp)->sclk_rate) struct mcp *mcp_host_alloc(struct device *, size_t); -int mcp_host_add(struct mcp *); +int mcp_host_add(struct mcp *, void *); void mcp_host_del(struct mcp *); void mcp_host_free(struct mcp *); diff --git a/include/linux/mfd/ucb1x00.h b/include/linux/mfd/ucb1x00.h index 4321f044d1e4..731b23a656c0 100644 --- a/include/linux/mfd/ucb1x00.h +++ b/include/linux/mfd/ucb1x00.h @@ -104,6 +104,9 @@ #define UCB_MODE_DYN_VFLAG_ENA (1 << 12) #define UCB_MODE_AUD_OFF_CAN (1 << 13) +struct ucb1x00_plat_data { + int gpio_base; +}; struct ucb1x00_irq { void *devid; -- cgit v1.2.3 From 2f7510c6070932371e0b842a5470ce7190dcf162 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 22 Jan 2012 19:02:25 +0000 Subject: MFD: ucb1x00-core: add handling for ucb1x00 reset Provide a way to handle the software controlled ucb1x00 reset signal from the ucb1x00-core driver without having to code platform specifics into these drivers. Acked-by: Jochen Friedrich Signed-off-by: Russell King --- drivers/mfd/ucb1x00-core.c | 17 +++++++++++++---- include/linux/mfd/ucb1x00.h | 7 +++++++ 2 files changed, 20 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/drivers/mfd/ucb1x00-core.c b/drivers/mfd/ucb1x00-core.c index cc1c0dce7bd9..42eee633b864 100644 --- a/drivers/mfd/ucb1x00-core.c +++ b/drivers/mfd/ucb1x00-core.c @@ -530,13 +530,17 @@ static struct class ucb1x00_class = { static int ucb1x00_probe(struct mcp *mcp) { - struct ucb1x00 *ucb; + struct ucb1x00_plat_data *pdata = mcp->attached_device.platform_data; struct ucb1x00_driver *drv; - struct ucb1x00_plat_data *pdata; + struct ucb1x00 *ucb; unsigned int id; int ret = -ENODEV; int temp; + /* Tell the platform to deassert the UCB1x00 reset */ + if (pdata && pdata->reset) + pdata->reset(UCB_RST_PROBE); + mcp_enable(mcp); id = mcp_reg_read(mcp, UCB_ID); @@ -550,7 +554,6 @@ static int ucb1x00_probe(struct mcp *mcp) if (!ucb) goto err_disable; - pdata = mcp->attached_device.platform_data; ucb->dev.class = &ucb1x00_class; ucb->dev.parent = &mcp->attached_device; dev_set_name(&ucb->dev, "ucb1x00"); @@ -606,7 +609,7 @@ static int ucb1x00_probe(struct mcp *mcp) } mutex_unlock(&ucb1x00_mutex); - goto out; + return ret; err_irq: free_irq(ucb->irq, ucb); @@ -618,11 +621,14 @@ static int ucb1x00_probe(struct mcp *mcp) err_disable: mcp_disable(mcp); out: + if (pdata && pdata->reset) + pdata->reset(UCB_RST_PROBE_FAIL); return ret; } static void ucb1x00_remove(struct mcp *mcp) { + struct ucb1x00_plat_data *pdata = mcp->attached_device.platform_data; struct ucb1x00 *ucb = mcp_get_drvdata(mcp); struct list_head *l, *n; int ret; @@ -643,6 +649,9 @@ static void ucb1x00_remove(struct mcp *mcp) free_irq(ucb->irq, ucb); device_unregister(&ucb->dev); + + if (pdata && pdata->reset) + pdata->reset(UCB_RST_REMOVE); } int ucb1x00_register_driver(struct ucb1x00_driver *drv) diff --git a/include/linux/mfd/ucb1x00.h b/include/linux/mfd/ucb1x00.h index 731b23a656c0..fd088cc6a4ca 100644 --- a/include/linux/mfd/ucb1x00.h +++ b/include/linux/mfd/ucb1x00.h @@ -104,7 +104,14 @@ #define UCB_MODE_DYN_VFLAG_ENA (1 << 12) #define UCB_MODE_AUD_OFF_CAN (1 << 13) +enum ucb1x00_reset { + UCB_RST_PROBE, + UCB_RST_REMOVE, + UCB_RST_PROBE_FAIL, +}; + struct ucb1x00_plat_data { + void (*reset)(enum ucb1x00_reset); int gpio_base; }; -- cgit v1.2.3 From cae154767a96563d33924872aacfdc63d584f707 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 21 Jan 2012 09:33:38 +0000 Subject: MFD: ucb1x00-core: use mutexes instead of semaphores Convert the ucb1x00 driver to use mutexes rather than the depreciated semaphores for exclusive access to the ADC. Acked-by: Jochen Friedrich Signed-off-by: Russell King --- drivers/mfd/ucb1x00-core.c | 15 +++++++-------- include/linux/mfd/ucb1x00.h | 4 ++-- 2 files changed, 9 insertions(+), 10 deletions(-) (limited to 'include/linux') diff --git a/drivers/mfd/ucb1x00-core.c b/drivers/mfd/ucb1x00-core.c index c2757c11ce7b..7386f822d4cd 100644 --- a/drivers/mfd/ucb1x00-core.c +++ b/drivers/mfd/ucb1x00-core.c @@ -27,7 +27,6 @@ #include #include #include -#include static DEFINE_MUTEX(ucb1x00_mutex); static LIST_HEAD(ucb1x00_drivers); @@ -99,7 +98,7 @@ void ucb1x00_io_write(struct ucb1x00 *ucb, unsigned int set, unsigned int clear) * ucb1x00_enable must have been called to enable the comms * before using this function. * - * This function does not take any semaphores or spinlocks. + * This function does not take any mutexes or spinlocks. */ unsigned int ucb1x00_io_read(struct ucb1x00 *ucb) { @@ -183,7 +182,7 @@ static int ucb1x00_gpio_direction_output(struct gpio_chip *chip, unsigned offset * Any code wishing to use the ADC converter must call this * function prior to using it. * - * This function takes the ADC semaphore to prevent two or more + * This function takes the ADC mutex to prevent two or more * concurrent uses, and therefore may sleep. As a result, it * can only be called from process context, not interrupt * context. @@ -193,7 +192,7 @@ static int ucb1x00_gpio_direction_output(struct gpio_chip *chip, unsigned offset */ void ucb1x00_adc_enable(struct ucb1x00 *ucb) { - down(&ucb->adc_sem); + mutex_lock(&ucb->adc_mutex); ucb->adc_cr |= UCB_ADC_ENA; @@ -215,7 +214,7 @@ void ucb1x00_adc_enable(struct ucb1x00 *ucb) * complete (2 frames max without sync). * * If called for a synchronised ADC conversion, it may sleep - * with the ADC semaphore held. + * with the ADC mutex held. */ unsigned int ucb1x00_adc_read(struct ucb1x00 *ucb, int adc_channel, int sync) { @@ -243,7 +242,7 @@ unsigned int ucb1x00_adc_read(struct ucb1x00 *ucb, int adc_channel, int sync) * ucb1x00_adc_disable - disable the ADC converter * @ucb: UCB1x00 structure describing chip * - * Disable the ADC converter and release the ADC semaphore. + * Disable the ADC converter and release the ADC mutex. */ void ucb1x00_adc_disable(struct ucb1x00 *ucb) { @@ -251,7 +250,7 @@ void ucb1x00_adc_disable(struct ucb1x00 *ucb) ucb1x00_reg_write(ucb, UCB_ADC_CR, ucb->adc_cr); ucb1x00_disable(ucb); - up(&ucb->adc_sem); + mutex_unlock(&ucb->adc_mutex); } /* @@ -560,7 +559,7 @@ static int ucb1x00_probe(struct mcp *mcp) spin_lock_init(&ucb->lock); spin_lock_init(&ucb->io_lock); - sema_init(&ucb->adc_sem, 1); + mutex_init(&ucb->adc_mutex); ucb->id = id; ucb->mcp = mcp; diff --git a/include/linux/mfd/ucb1x00.h b/include/linux/mfd/ucb1x00.h index fd088cc6a4ca..a4b954381c2f 100644 --- a/include/linux/mfd/ucb1x00.h +++ b/include/linux/mfd/ucb1x00.h @@ -12,7 +12,7 @@ #include #include -#include +#include #define UCB_IO_DATA 0x00 #define UCB_IO_DIR 0x01 @@ -124,7 +124,7 @@ struct ucb1x00 { spinlock_t lock; struct mcp *mcp; unsigned int irq; - struct semaphore adc_sem; + struct mutex adc_mutex; spinlock_t io_lock; u16 id; u16 io_dir; -- cgit v1.2.3 From 5a09b7120a965a7d7e8494d0ed509135bbce0118 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 21 Jan 2012 16:36:30 +0000 Subject: MFD: ucb1x00-core: convert to use dev_pm_ops Convert the ucb1x00-core driver to use dev_pm_ops rather than the legacy members in the mcp driver. Acked-by: Jochen Friedrich Signed-off-by: Russell King --- drivers/mfd/ucb1x00-core.c | 32 ++++++++++++++++++-------------- include/linux/mfd/ucb1x00.h | 2 +- 2 files changed, 19 insertions(+), 15 deletions(-) (limited to 'include/linux') diff --git a/drivers/mfd/ucb1x00-core.c b/drivers/mfd/ucb1x00-core.c index ed2a4b2e518f..6fab82557543 100644 --- a/drivers/mfd/ucb1x00-core.c +++ b/drivers/mfd/ucb1x00-core.c @@ -26,6 +26,7 @@ #include #include #include +#include #include static DEFINE_MUTEX(ucb1x00_mutex); @@ -697,47 +698,50 @@ void ucb1x00_unregister_driver(struct ucb1x00_driver *drv) mutex_unlock(&ucb1x00_mutex); } -static int ucb1x00_suspend(struct mcp *mcp, pm_message_t state) +static int ucb1x00_suspend(struct device *dev) { - struct ucb1x00 *ucb = mcp_get_drvdata(mcp); - struct ucb1x00_dev *dev; + struct ucb1x00 *ucb = dev_get_drvdata(dev); + struct ucb1x00_dev *udev; mutex_lock(&ucb1x00_mutex); - list_for_each_entry(dev, &ucb->devs, dev_node) { - if (dev->drv->suspend) - dev->drv->suspend(dev, state); + list_for_each_entry(udev, &ucb->devs, dev_node) { + if (udev->drv->suspend) + udev->drv->suspend(udev); } mutex_unlock(&ucb1x00_mutex); return 0; } -static int ucb1x00_resume(struct mcp *mcp) +static int ucb1x00_resume(struct device *dev) { - struct ucb1x00 *ucb = mcp_get_drvdata(mcp); - struct ucb1x00_dev *dev; + struct ucb1x00 *ucb = dev_get_drvdata(dev); + struct ucb1x00_dev *udev; ucb1x00_enable(ucb); ucb1x00_reg_write(ucb, UCB_IO_DATA, ucb->io_out); ucb1x00_reg_write(ucb, UCB_IO_DIR, ucb->io_dir); ucb1x00_disable(ucb); mutex_lock(&ucb1x00_mutex); - list_for_each_entry(dev, &ucb->devs, dev_node) { - if (dev->drv->resume) - dev->drv->resume(dev); + list_for_each_entry(udev, &ucb->devs, dev_node) { + if (udev->drv->resume) + udev->drv->resume(udev); } mutex_unlock(&ucb1x00_mutex); return 0; } +static const struct dev_pm_ops ucb1x00_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(ucb1x00_suspend, ucb1x00_resume) +}; + static struct mcp_driver ucb1x00_driver = { .drv = { .name = "ucb1x00", .owner = THIS_MODULE, + .pm = &ucb1x00_pm_ops, }, .probe = ucb1x00_probe, .remove = ucb1x00_remove, - .suspend = ucb1x00_suspend, - .resume = ucb1x00_resume, }; static int __init ucb1x00_init(void) diff --git a/include/linux/mfd/ucb1x00.h b/include/linux/mfd/ucb1x00.h index a4b954381c2f..253c12c157a6 100644 --- a/include/linux/mfd/ucb1x00.h +++ b/include/linux/mfd/ucb1x00.h @@ -154,7 +154,7 @@ struct ucb1x00_driver { struct list_head devs; int (*add)(struct ucb1x00_dev *dev); void (*remove)(struct ucb1x00_dev *dev); - int (*suspend)(struct ucb1x00_dev *dev, pm_message_t state); + int (*suspend)(struct ucb1x00_dev *dev); int (*resume)(struct ucb1x00_dev *dev); }; -- cgit v1.2.3 From cf4abfcc0df2985ff6061f74e63b8353f2a1d0bc Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 21 Jan 2012 16:38:50 +0000 Subject: MFD: mcp-core: remove legacy driver suspend/resume methods The legacy driver suspend/resume methods are no longer used, so get rid of them. Acked-by: Jochen Friedrich Signed-off-by: Russell King --- drivers/mfd/mcp-core.c | 28 ---------------------------- include/linux/mfd/mcp.h | 2 -- 2 files changed, 30 deletions(-) (limited to 'include/linux') diff --git a/drivers/mfd/mcp-core.c b/drivers/mfd/mcp-core.c index c409d6327140..6acf2e03f2ba 100644 --- a/drivers/mfd/mcp-core.c +++ b/drivers/mfd/mcp-core.c @@ -47,39 +47,11 @@ static int mcp_bus_remove(struct device *dev) return 0; } -static int mcp_bus_suspend(struct device *dev, pm_message_t state) -{ - struct mcp *mcp = to_mcp(dev); - int ret = 0; - - if (dev->driver) { - struct mcp_driver *drv = to_mcp_driver(dev->driver); - - ret = drv->suspend(mcp, state); - } - return ret; -} - -static int mcp_bus_resume(struct device *dev) -{ - struct mcp *mcp = to_mcp(dev); - int ret = 0; - - if (dev->driver) { - struct mcp_driver *drv = to_mcp_driver(dev->driver); - - ret = drv->resume(mcp); - } - return ret; -} - static struct bus_type mcp_bus_type = { .name = "mcp", .match = mcp_bus_match, .probe = mcp_bus_probe, .remove = mcp_bus_remove, - .suspend = mcp_bus_suspend, - .resume = mcp_bus_resume, }; /** diff --git a/include/linux/mfd/mcp.h b/include/linux/mfd/mcp.h index bfcdf6d3f1bf..a9e8bd157673 100644 --- a/include/linux/mfd/mcp.h +++ b/include/linux/mfd/mcp.h @@ -48,8 +48,6 @@ struct mcp_driver { struct device_driver drv; int (*probe)(struct mcp *); void (*remove)(struct mcp *); - int (*suspend)(struct mcp *, pm_message_t); - int (*resume)(struct mcp *); }; int mcp_driver_register(struct mcp_driver *); -- cgit v1.2.3 From a3364409c4af8bae42d04def48dc11409787e503 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 21 Jan 2012 14:58:28 +0000 Subject: MFD: ucb1x00: convert to use genirq Convert the ucb1x00 driver to use genirq's interrupt services, rather than its own private implementation. This allows a wider range of drivers to use the GPIO interrupts (such as the gpio_keys driver) without being aware of the UCB1x00's private IRQ system. This prevents the UCB1x00 core driver from being built as a module, so adjust the configuration to add that restriction. Acked-by: Jochen Friedrich Signed-off-by: Russell King --- drivers/mfd/Kconfig | 5 +- drivers/mfd/ucb1x00-core.c | 247 +++++++++++++++++--------------------------- drivers/mfd/ucb1x00-ts.c | 37 +++++-- include/linux/mfd/ucb1x00.h | 22 +--- 4 files changed, 132 insertions(+), 179 deletions(-) (limited to 'include/linux') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index cd13e9f2f5e6..28a301b28579 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -847,8 +847,9 @@ config MCP_SA11X0 # Chip drivers config MCP_UCB1200 - tristate "Support for UCB1200 / UCB1300" - depends on MCP + bool "Support for UCB1200 / UCB1300" + depends on MCP_SA11X0 + select MCP config MCP_UCB1200_TS tristate "Touchscreen interface support" diff --git a/drivers/mfd/ucb1x00-core.c b/drivers/mfd/ucb1x00-core.c index 6fab82557543..400604d38780 100644 --- a/drivers/mfd/ucb1x00-core.c +++ b/drivers/mfd/ucb1x00-core.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -178,6 +179,13 @@ static int ucb1x00_gpio_direction_output(struct gpio_chip *chip, unsigned offset return 0; } +static int ucb1x00_to_irq(struct gpio_chip *chip, unsigned offset) +{ + struct ucb1x00 *ucb = container_of(chip, struct ucb1x00, gpio); + + return ucb->irq_base > 0 ? ucb->irq_base + offset : -ENXIO; +} + /* * UCB1300 data sheet says we must: * 1. enable ADC => 5us (including reference startup time) @@ -274,10 +282,9 @@ void ucb1x00_adc_disable(struct ucb1x00 *ucb) * SIBCLK to talk to the chip. We leave the clock running until * we have finished processing all interrupts from the chip. */ -static irqreturn_t ucb1x00_irq(int irqnr, void *devid) +static void ucb1x00_irq(unsigned int irq, struct irq_desc *desc) { - struct ucb1x00 *ucb = devid; - struct ucb1x00_irq *irq; + struct ucb1x00 *ucb = irq_desc_get_handler_data(desc); unsigned int isr, i; ucb1x00_enable(ucb); @@ -285,157 +292,84 @@ static irqreturn_t ucb1x00_irq(int irqnr, void *devid) ucb1x00_reg_write(ucb, UCB_IE_CLEAR, isr); ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0); - for (i = 0, irq = ucb->irq_handler; i < 16 && isr; i++, isr >>= 1, irq++) - if (isr & 1 && irq->fn) - irq->fn(i, irq->devid); + for (i = 0; i < 16 && isr; i++, isr >>= 1, irq++) + if (isr & 1) + generic_handle_irq(ucb->irq_base + i); ucb1x00_disable(ucb); - - return IRQ_HANDLED; } -/** - * ucb1x00_hook_irq - hook a UCB1x00 interrupt - * @ucb: UCB1x00 structure describing chip - * @idx: interrupt index - * @fn: function to call when interrupt is triggered - * @devid: device id to pass to interrupt handler - * - * Hook the specified interrupt. You can only register one handler - * for each interrupt source. The interrupt source is not enabled - * by this function; use ucb1x00_enable_irq instead. - * - * Interrupt handlers will be called with other interrupts enabled. - * - * Returns zero on success, or one of the following errors: - * -EINVAL if the interrupt index is invalid - * -EBUSY if the interrupt has already been hooked - */ -int ucb1x00_hook_irq(struct ucb1x00 *ucb, unsigned int idx, void (*fn)(int, void *), void *devid) +static void ucb1x00_irq_update(struct ucb1x00 *ucb, unsigned mask) { - struct ucb1x00_irq *irq; - int ret = -EINVAL; - - if (idx < 16) { - irq = ucb->irq_handler + idx; - ret = -EBUSY; - - spin_lock_irq(&ucb->lock); - if (irq->fn == NULL) { - irq->devid = devid; - irq->fn = fn; - ret = 0; - } - spin_unlock_irq(&ucb->lock); - } - return ret; + ucb1x00_enable(ucb); + if (ucb->irq_ris_enbl & mask) + ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl & + ucb->irq_mask); + if (ucb->irq_fal_enbl & mask) + ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl & + ucb->irq_mask); + ucb1x00_disable(ucb); } -/** - * ucb1x00_enable_irq - enable an UCB1x00 interrupt source - * @ucb: UCB1x00 structure describing chip - * @idx: interrupt index - * @edges: interrupt edges to enable - * - * Enable the specified interrupt to trigger on %UCB_RISING, - * %UCB_FALLING or both edges. The interrupt should have been - * hooked by ucb1x00_hook_irq. - */ -void ucb1x00_enable_irq(struct ucb1x00 *ucb, unsigned int idx, int edges) +static void ucb1x00_irq_noop(struct irq_data *data) { - unsigned long flags; - - if (idx < 16) { - spin_lock_irqsave(&ucb->lock, flags); - - ucb1x00_enable(ucb); - if (edges & UCB_RISING) { - ucb->irq_ris_enbl |= 1 << idx; - ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl); - } - if (edges & UCB_FALLING) { - ucb->irq_fal_enbl |= 1 << idx; - ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl); - } - ucb1x00_disable(ucb); - spin_unlock_irqrestore(&ucb->lock, flags); - } } -/** - * ucb1x00_disable_irq - disable an UCB1x00 interrupt source - * @ucb: UCB1x00 structure describing chip - * @edges: interrupt edges to disable - * - * Disable the specified interrupt triggering on the specified - * (%UCB_RISING, %UCB_FALLING or both) edges. - */ -void ucb1x00_disable_irq(struct ucb1x00 *ucb, unsigned int idx, int edges) +static void ucb1x00_irq_mask(struct irq_data *data) { - unsigned long flags; + struct ucb1x00 *ucb = irq_data_get_irq_chip_data(data); + unsigned mask = 1 << (data->irq - ucb->irq_base); - if (idx < 16) { - spin_lock_irqsave(&ucb->lock, flags); - - ucb1x00_enable(ucb); - if (edges & UCB_RISING) { - ucb->irq_ris_enbl &= ~(1 << idx); - ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl); - } - if (edges & UCB_FALLING) { - ucb->irq_fal_enbl &= ~(1 << idx); - ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl); - } - ucb1x00_disable(ucb); - spin_unlock_irqrestore(&ucb->lock, flags); - } + raw_spin_lock(&ucb->irq_lock); + ucb->irq_mask &= ~mask; + ucb1x00_irq_update(ucb, mask); + raw_spin_unlock(&ucb->irq_lock); } -/** - * ucb1x00_free_irq - disable and free the specified UCB1x00 interrupt - * @ucb: UCB1x00 structure describing chip - * @idx: interrupt index - * @devid: device id. - * - * Disable the interrupt source and remove the handler. devid must - * match the devid passed when hooking the interrupt. - * - * Returns zero on success, or one of the following errors: - * -EINVAL if the interrupt index is invalid - * -ENOENT if devid does not match - */ -int ucb1x00_free_irq(struct ucb1x00 *ucb, unsigned int idx, void *devid) +static void ucb1x00_irq_unmask(struct irq_data *data) { - struct ucb1x00_irq *irq; - int ret; + struct ucb1x00 *ucb = irq_data_get_irq_chip_data(data); + unsigned mask = 1 << (data->irq - ucb->irq_base); - if (idx >= 16) - goto bad; - - irq = ucb->irq_handler + idx; - ret = -ENOENT; + raw_spin_lock(&ucb->irq_lock); + ucb->irq_mask |= mask; + ucb1x00_irq_update(ucb, mask); + raw_spin_unlock(&ucb->irq_lock); +} - spin_lock_irq(&ucb->lock); - if (irq->devid == devid) { - ucb->irq_ris_enbl &= ~(1 << idx); - ucb->irq_fal_enbl &= ~(1 << idx); +static int ucb1x00_irq_set_type(struct irq_data *data, unsigned int type) +{ + struct ucb1x00 *ucb = irq_data_get_irq_chip_data(data); + unsigned mask = 1 << (data->irq - ucb->irq_base); - ucb1x00_enable(ucb); - ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl); - ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl); - ucb1x00_disable(ucb); + raw_spin_lock(&ucb->irq_lock); + if (type & IRQ_TYPE_EDGE_RISING) + ucb->irq_ris_enbl |= mask; + else + ucb->irq_ris_enbl &= ~mask; - irq->fn = NULL; - irq->devid = NULL; - ret = 0; + if (type & IRQ_TYPE_EDGE_FALLING) + ucb->irq_fal_enbl |= mask; + else + ucb->irq_fal_enbl &= ~mask; + if (ucb->irq_mask & mask) { + ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl & + ucb->irq_mask); + ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl & + ucb->irq_mask); } - spin_unlock_irq(&ucb->lock); - return ret; + raw_spin_unlock(&ucb->irq_lock); -bad: - printk(KERN_ERR "Freeing bad UCB1x00 irq %d\n", idx); - return -EINVAL; + return 0; } +static struct irq_chip ucb1x00_irqchip = { + .name = "ucb1x00", + .irq_ack = ucb1x00_irq_noop, + .irq_mask = ucb1x00_irq_mask, + .irq_unmask = ucb1x00_irq_unmask, + .irq_set_type = ucb1x00_irq_set_type, +}; + static int ucb1x00_add_dev(struct ucb1x00 *ucb, struct ucb1x00_driver *drv) { struct ucb1x00_dev *dev; @@ -545,9 +479,8 @@ static int ucb1x00_probe(struct mcp *mcp) struct ucb1x00_plat_data *pdata = mcp->attached_device.platform_data; struct ucb1x00_driver *drv; struct ucb1x00 *ucb; - unsigned int id; + unsigned id, i, irq_base; int ret = -ENODEV; - int temp; /* Tell the platform to deassert the UCB1x00 reset */ if (pdata && pdata->reset) @@ -572,7 +505,7 @@ static int ucb1x00_probe(struct mcp *mcp) ucb->dev.parent = &mcp->attached_device; dev_set_name(&ucb->dev, "ucb1x00"); - spin_lock_init(&ucb->lock); + raw_spin_lock_init(&ucb->irq_lock); spin_lock_init(&ucb->io_lock); mutex_init(&ucb->adc_mutex); @@ -593,6 +526,26 @@ static int ucb1x00_probe(struct mcp *mcp) } ucb->gpio.base = -1; + irq_base = pdata ? pdata->irq_base : 0; + ucb->irq_base = irq_alloc_descs(-1, irq_base, 16, -1); + if (ucb->irq_base < 0) { + dev_err(&ucb->dev, "unable to allocate 16 irqs: %d\n", + ucb->irq_base); + goto err_irq_alloc; + } + + for (i = 0; i < 16; i++) { + unsigned irq = ucb->irq_base + i; + + irq_set_chip_and_handler(irq, &ucb1x00_irqchip, handle_edge_irq); + irq_set_chip_data(irq, ucb); + set_irq_flags(irq, IRQF_VALID | IRQ_NOREQUEST); + } + + irq_set_irq_type(ucb->irq, IRQ_TYPE_EDGE_RISING); + irq_set_handler_data(ucb->irq, ucb); + irq_set_chained_handler(ucb->irq, ucb1x00_irq); + if (pdata && pdata->gpio_base) { ucb->gpio.label = dev_name(&ucb->dev); ucb->gpio.dev = &ucb->dev; @@ -603,20 +556,13 @@ static int ucb1x00_probe(struct mcp *mcp) ucb->gpio.get = ucb1x00_gpio_get; ucb->gpio.direction_input = ucb1x00_gpio_direction_input; ucb->gpio.direction_output = ucb1x00_gpio_direction_output; + ucb->gpio.to_irq = ucb1x00_to_irq; ret = gpiochip_add(&ucb->gpio); if (ret) goto err_gpio_add; } else dev_info(&ucb->dev, "gpio_base not set so no gpiolib support"); - ret = request_irq(ucb->irq, ucb1x00_irq, IRQF_TRIGGER_RISING, - "UCB1x00", ucb); - if (ret) { - dev_err(&ucb->dev, "ucb1x00: unable to grab irq%d: %d\n", - ucb->irq, ret); - goto err_irq; - } - mcp_set_drvdata(mcp, ucb); INIT_LIST_HEAD(&ucb->devs); @@ -629,10 +575,11 @@ static int ucb1x00_probe(struct mcp *mcp) return ret; - err_irq: - if (ucb->gpio.base != -1) - temp = gpiochip_remove(&ucb->gpio); err_gpio_add: + irq_set_chained_handler(ucb->irq, NULL); + err_irq_alloc: + if (ucb->irq_base > 0) + irq_free_descs(ucb->irq_base, 16); err_no_irq: device_del(&ucb->dev); err_dev_add: @@ -664,7 +611,8 @@ static void ucb1x00_remove(struct mcp *mcp) dev_err(&ucb->dev, "Can't remove gpio chip: %d\n", ret); } - free_irq(ucb->irq, ucb); + irq_set_chained_handler(ucb->irq, NULL); + irq_free_descs(ucb->irq_base, 16); device_unregister(&ucb->dev); if (pdata && pdata->reset) @@ -772,11 +720,6 @@ EXPORT_SYMBOL(ucb1x00_adc_enable); EXPORT_SYMBOL(ucb1x00_adc_read); EXPORT_SYMBOL(ucb1x00_adc_disable); -EXPORT_SYMBOL(ucb1x00_hook_irq); -EXPORT_SYMBOL(ucb1x00_free_irq); -EXPORT_SYMBOL(ucb1x00_enable_irq); -EXPORT_SYMBOL(ucb1x00_disable_irq); - EXPORT_SYMBOL(ucb1x00_register_driver); EXPORT_SYMBOL(ucb1x00_unregister_driver); diff --git a/drivers/mfd/ucb1x00-ts.c b/drivers/mfd/ucb1x00-ts.c index 742d0c7bbbc2..1e0e20c0e082 100644 --- a/drivers/mfd/ucb1x00-ts.c +++ b/drivers/mfd/ucb1x00-ts.c @@ -20,8 +20,9 @@ #include #include #include -#include +#include #include +#include #include #include #include @@ -41,6 +42,8 @@ struct ucb1x00_ts { struct input_dev *idev; struct ucb1x00 *ucb; + spinlock_t irq_lock; + unsigned irq_disabled; wait_queue_head_t irq_wait; struct task_struct *rtask; u16 x_res; @@ -237,7 +240,12 @@ static int ucb1x00_thread(void *_ts) if (ucb1x00_ts_pen_down(ts)) { set_current_state(TASK_INTERRUPTIBLE); - ucb1x00_enable_irq(ts->ucb, UCB_IRQ_TSPX, machine_is_collie() ? UCB_RISING : UCB_FALLING); + spin_lock_irq(&ts->irq_lock); + if (ts->irq_disabled) { + ts->irq_disabled = 0; + enable_irq(ts->ucb->irq_base + UCB_IRQ_TSPX); + } + spin_unlock_irq(&ts->irq_lock); ucb1x00_disable(ts->ucb); /* @@ -280,23 +288,37 @@ static int ucb1x00_thread(void *_ts) * We only detect touch screen _touches_ with this interrupt * handler, and even then we just schedule our task. */ -static void ucb1x00_ts_irq(int idx, void *id) +static irqreturn_t ucb1x00_ts_irq(int irq, void *id) { struct ucb1x00_ts *ts = id; - ucb1x00_disable_irq(ts->ucb, UCB_IRQ_TSPX, UCB_FALLING); + spin_lock(&ts->irq_lock); + ts->irq_disabled = 1; + disable_irq_nosync(ts->ucb->irq_base + UCB_IRQ_TSPX); + spin_unlock(&ts->irq_lock); wake_up(&ts->irq_wait); + + return IRQ_HANDLED; } static int ucb1x00_ts_open(struct input_dev *idev) { struct ucb1x00_ts *ts = input_get_drvdata(idev); + unsigned long flags = 0; int ret = 0; BUG_ON(ts->rtask); + if (machine_is_collie()) + flags = IRQF_TRIGGER_RISING; + else + flags = IRQF_TRIGGER_FALLING; + + ts->irq_disabled = 0; + init_waitqueue_head(&ts->irq_wait); - ret = ucb1x00_hook_irq(ts->ucb, UCB_IRQ_TSPX, ucb1x00_ts_irq, ts); + ret = request_irq(ts->ucb->irq_base + UCB_IRQ_TSPX, ucb1x00_ts_irq, + flags, "ucb1x00-ts", ts); if (ret < 0) goto out; @@ -313,7 +335,7 @@ static int ucb1x00_ts_open(struct input_dev *idev) if (!IS_ERR(ts->rtask)) { ret = 0; } else { - ucb1x00_free_irq(ts->ucb, UCB_IRQ_TSPX, ts); + free_irq(ts->ucb->irq_base + UCB_IRQ_TSPX, ts); ts->rtask = NULL; ret = -EFAULT; } @@ -333,7 +355,7 @@ static void ucb1x00_ts_close(struct input_dev *idev) kthread_stop(ts->rtask); ucb1x00_enable(ts->ucb); - ucb1x00_free_irq(ts->ucb, UCB_IRQ_TSPX, ts); + free_irq(ts->ucb->irq_base + UCB_IRQ_TSPX, ts); ucb1x00_reg_write(ts->ucb, UCB_TS_CR, 0); ucb1x00_disable(ts->ucb); } @@ -358,6 +380,7 @@ static int ucb1x00_ts_add(struct ucb1x00_dev *dev) ts->ucb = dev->ucb; ts->idev = idev; ts->adcsync = adcsync ? UCB_SYNC : UCB_NOSYNC; + spin_lock_init(&ts->irq_lock); idev->name = "Touchscreen panel"; idev->id.product = ts->ucb->id; diff --git a/include/linux/mfd/ucb1x00.h b/include/linux/mfd/ucb1x00.h index 253c12c157a6..6fb907446c33 100644 --- a/include/linux/mfd/ucb1x00.h +++ b/include/linux/mfd/ucb1x00.h @@ -112,18 +112,15 @@ enum ucb1x00_reset { struct ucb1x00_plat_data { void (*reset)(enum ucb1x00_reset); + unsigned irq_base; int gpio_base; }; -struct ucb1x00_irq { - void *devid; - void (*fn)(int, void *); -}; - struct ucb1x00 { - spinlock_t lock; + raw_spinlock_t irq_lock; struct mcp *mcp; unsigned int irq; + int irq_base; struct mutex adc_mutex; spinlock_t io_lock; u16 id; @@ -132,7 +129,7 @@ struct ucb1x00 { u16 adc_cr; u16 irq_fal_enbl; u16 irq_ris_enbl; - struct ucb1x00_irq irq_handler[16]; + u16 irq_mask; struct device dev; struct list_head node; struct list_head devs; @@ -255,15 +252,4 @@ unsigned int ucb1x00_adc_read(struct ucb1x00 *ucb, int adc_channel, int sync); void ucb1x00_adc_enable(struct ucb1x00 *ucb); void ucb1x00_adc_disable(struct ucb1x00 *ucb); -/* - * Which edges of the IRQ do you want to control today? - */ -#define UCB_RISING (1 << 0) -#define UCB_FALLING (1 << 1) - -int ucb1x00_hook_irq(struct ucb1x00 *ucb, unsigned int idx, void (*fn)(int, void *), void *devid); -void ucb1x00_enable_irq(struct ucb1x00 *ucb, unsigned int idx, int edges); -void ucb1x00_disable_irq(struct ucb1x00 *ucb, unsigned int idx, int edges); -int ucb1x00_free_irq(struct ucb1x00 *ucb, unsigned int idx, void *devid); - #endif -- cgit v1.2.3 From 33237616771bfc29a97f17e74efe3799bb790343 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 22 Jan 2012 20:05:24 +0000 Subject: MFD: ucb1x00-core: add wakeup support Add genirq wakeup support for the ucb1x00 device. This allows an attached gpio_keys driver to wakeup the system. Touchscreen is also possible. When there are no wakeup sources, ask the platform to assert the reset signal to avoid any unexpected behaviour; this also puts the reset signal at the right level when power is removed from the device. Acked-by: Jochen Friedrich Signed-off-by: Russell King --- drivers/mfd/ucb1x00-core.c | 59 +++++++++++++++++++++++++++++++++++++++++++++ include/linux/mfd/ucb1x00.h | 4 +++ 2 files changed, 63 insertions(+) (limited to 'include/linux') diff --git a/drivers/mfd/ucb1x00-core.c b/drivers/mfd/ucb1x00-core.c index 400604d38780..70f02daeb22a 100644 --- a/drivers/mfd/ucb1x00-core.c +++ b/drivers/mfd/ucb1x00-core.c @@ -362,12 +362,32 @@ static int ucb1x00_irq_set_type(struct irq_data *data, unsigned int type) return 0; } +static int ucb1x00_irq_set_wake(struct irq_data *data, unsigned int on) +{ + struct ucb1x00 *ucb = irq_data_get_irq_chip_data(data); + struct ucb1x00_plat_data *pdata = ucb->mcp->attached_device.platform_data; + unsigned mask = 1 << (data->irq - ucb->irq_base); + + if (!pdata || !pdata->can_wakeup) + return -EINVAL; + + raw_spin_lock(&ucb->irq_lock); + if (on) + ucb->irq_wake |= mask; + else + ucb->irq_wake &= ~mask; + raw_spin_unlock(&ucb->irq_lock); + + return 0; +} + static struct irq_chip ucb1x00_irqchip = { .name = "ucb1x00", .irq_ack = ucb1x00_irq_noop, .irq_mask = ucb1x00_irq_mask, .irq_unmask = ucb1x00_irq_unmask, .irq_set_type = ucb1x00_irq_set_type, + .irq_set_wake = ucb1x00_irq_set_wake, }; static int ucb1x00_add_dev(struct ucb1x00 *ucb, struct ucb1x00_driver *drv) @@ -565,6 +585,9 @@ static int ucb1x00_probe(struct mcp *mcp) mcp_set_drvdata(mcp, ucb); + if (pdata) + device_set_wakeup_capable(&ucb->dev, pdata->can_wakeup); + INIT_LIST_HEAD(&ucb->devs); mutex_lock(&ucb1x00_mutex); list_add_tail(&ucb->node, &ucb1x00_devices); @@ -648,6 +671,7 @@ void ucb1x00_unregister_driver(struct ucb1x00_driver *drv) static int ucb1x00_suspend(struct device *dev) { + struct ucb1x00_plat_data *pdata = dev->platform_data; struct ucb1x00 *ucb = dev_get_drvdata(dev); struct ucb1x00_dev *udev; @@ -657,18 +681,53 @@ static int ucb1x00_suspend(struct device *dev) udev->drv->suspend(udev); } mutex_unlock(&ucb1x00_mutex); + + if (ucb->irq_wake) { + unsigned long flags; + + raw_spin_lock_irqsave(&ucb->irq_lock, flags); + ucb1x00_enable(ucb); + ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl & + ucb->irq_wake); + ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl & + ucb->irq_wake); + ucb1x00_disable(ucb); + raw_spin_unlock_irqrestore(&ucb->irq_lock, flags); + + enable_irq_wake(ucb->irq); + } else if (pdata && pdata->reset) + pdata->reset(UCB_RST_SUSPEND); + return 0; } static int ucb1x00_resume(struct device *dev) { + struct ucb1x00_plat_data *pdata = dev->platform_data; struct ucb1x00 *ucb = dev_get_drvdata(dev); struct ucb1x00_dev *udev; + if (!ucb->irq_wake && pdata && pdata->reset) + pdata->reset(UCB_RST_RESUME); + ucb1x00_enable(ucb); ucb1x00_reg_write(ucb, UCB_IO_DATA, ucb->io_out); ucb1x00_reg_write(ucb, UCB_IO_DIR, ucb->io_dir); + + if (ucb->irq_wake) { + unsigned long flags; + + raw_spin_lock_irqsave(&ucb->irq_lock, flags); + ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl & + ucb->irq_mask); + ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl & + ucb->irq_mask); + raw_spin_unlock_irqrestore(&ucb->irq_lock, flags); + + disable_irq_wake(ucb->irq); + } ucb1x00_disable(ucb); + mutex_lock(&ucb1x00_mutex); list_for_each_entry(udev, &ucb->devs, dev_node) { if (udev->drv->resume) diff --git a/include/linux/mfd/ucb1x00.h b/include/linux/mfd/ucb1x00.h index 6fb907446c33..28af41756360 100644 --- a/include/linux/mfd/ucb1x00.h +++ b/include/linux/mfd/ucb1x00.h @@ -106,6 +106,8 @@ enum ucb1x00_reset { UCB_RST_PROBE, + UCB_RST_RESUME, + UCB_RST_SUSPEND, UCB_RST_REMOVE, UCB_RST_PROBE_FAIL, }; @@ -114,6 +116,7 @@ struct ucb1x00_plat_data { void (*reset)(enum ucb1x00_reset); unsigned irq_base; int gpio_base; + unsigned can_wakeup; }; struct ucb1x00 { @@ -130,6 +133,7 @@ struct ucb1x00 { u16 irq_fal_enbl; u16 irq_ris_enbl; u16 irq_mask; + u16 irq_wake; struct device dev; struct list_head node; struct list_head devs; -- cgit v1.2.3 From 0c2022eccb01630c037f2024531e9ff1afbe1564 Mon Sep 17 00:00:00 2001 From: Yongqiang Yang Date: Mon, 20 Feb 2012 17:53:02 -0500 Subject: jbd2: allocate transaction from separate slab cache There is normally only a handful of these active at any one time, but putting them in a separate slab cache makes debugging memory corruption problems easier. Manish Katiyar also wanted this make it easier to test memory failure scenarios in the jbd2 layer. Cc: Manish Katiyar Signed-off-by: Yongqiang Yang Signed-off-by: "Theodore Ts'o" --- fs/jbd2/checkpoint.c | 2 +- fs/jbd2/commit.c | 2 +- fs/jbd2/journal.c | 3 +++ fs/jbd2/transaction.c | 36 +++++++++++++++++++++++++++++++++--- include/linux/jbd2.h | 5 +++++ 5 files changed, 43 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/fs/jbd2/checkpoint.c b/fs/jbd2/checkpoint.c index 453c9068b7d7..253e91890e71 100644 --- a/fs/jbd2/checkpoint.c +++ b/fs/jbd2/checkpoint.c @@ -722,7 +722,7 @@ int __jbd2_journal_remove_checkpoint(struct journal_head *jh) transaction->t_tid, stats); __jbd2_journal_drop_transaction(journal, transaction); - kfree(transaction); + jbd2_journal_free_transaction(transaction); /* Just in case anybody was waiting for more transactions to be checkpointed... */ diff --git a/fs/jbd2/commit.c b/fs/jbd2/commit.c index 5069b8475150..8adc5d460f56 100644 --- a/fs/jbd2/commit.c +++ b/fs/jbd2/commit.c @@ -1048,7 +1048,7 @@ restart_loop: jbd_debug(1, "JBD2: commit %d complete, head %d\n", journal->j_commit_sequence, journal->j_tail_sequence); if (to_free) - kfree(commit_transaction); + jbd2_journal_free_transaction(commit_transaction); wake_up(&journal->j_wait_done_commit); } diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c index 93a595c69616..aa8f5986f8da 100644 --- a/fs/jbd2/journal.c +++ b/fs/jbd2/journal.c @@ -2361,6 +2361,8 @@ static int __init journal_init_caches(void) ret = journal_init_jbd2_journal_head_cache(); if (ret == 0) ret = journal_init_handle_cache(); + if (ret == 0) + ret = jbd2_journal_init_transaction_cache(); return ret; } @@ -2369,6 +2371,7 @@ static void jbd2_journal_destroy_caches(void) jbd2_journal_destroy_revoke_caches(); jbd2_journal_destroy_jbd2_journal_head_cache(); jbd2_journal_destroy_handle_cache(); + jbd2_journal_destroy_transaction_cache(); jbd2_journal_destroy_slabs(); } diff --git a/fs/jbd2/transaction.c b/fs/jbd2/transaction.c index 526533062548..d0a8b017b281 100644 --- a/fs/jbd2/transaction.c +++ b/fs/jbd2/transaction.c @@ -33,6 +33,35 @@ static void __jbd2_journal_temp_unlink_buffer(struct journal_head *jh); static void __jbd2_journal_unfile_buffer(struct journal_head *jh); +static struct kmem_cache *transaction_cache; +int __init jbd2_journal_init_transaction_cache(void) +{ + J_ASSERT(!transaction_cache); + transaction_cache = kmem_cache_create("jbd2_transaction_s", + sizeof(transaction_t), + 0, + SLAB_HWCACHE_ALIGN|SLAB_TEMPORARY, + NULL); + if (transaction_cache) + return 0; + return -ENOMEM; +} + +void jbd2_journal_destroy_transaction_cache(void) +{ + if (transaction_cache) { + kmem_cache_destroy(transaction_cache); + transaction_cache = NULL; + } +} + +void jbd2_journal_free_transaction(transaction_t *transaction) +{ + if (unlikely(ZERO_OR_NULL_PTR(transaction))) + return; + kmem_cache_free(transaction_cache, transaction); +} + /* * jbd2_get_transaction: obtain a new transaction_t object. * @@ -133,7 +162,8 @@ static int start_this_handle(journal_t *journal, handle_t *handle, alloc_transaction: if (!journal->j_running_transaction) { - new_transaction = kzalloc(sizeof(*new_transaction), gfp_mask); + new_transaction = kmem_cache_alloc(transaction_cache, + gfp_mask | __GFP_ZERO); if (!new_transaction) { /* * If __GFP_FS is not present, then we may be @@ -162,7 +192,7 @@ repeat: if (is_journal_aborted(journal) || (journal->j_errno != 0 && !(journal->j_flags & JBD2_ACK_ERR))) { read_unlock(&journal->j_state_lock); - kfree(new_transaction); + jbd2_journal_free_transaction(new_transaction); return -EROFS; } @@ -284,7 +314,7 @@ repeat: read_unlock(&journal->j_state_lock); lock_map_acquire(&handle->h_lockdep_map); - kfree(new_transaction); + jbd2_journal_free_transaction(new_transaction); return 0; } diff --git a/include/linux/jbd2.h b/include/linux/jbd2.h index 5557baefed60..46eef77e6ab8 100644 --- a/include/linux/jbd2.h +++ b/include/linux/jbd2.h @@ -1020,6 +1020,11 @@ jbd2_journal_write_metadata_buffer(transaction_t *transaction, /* Transaction locking */ extern void __wait_on_journal (journal_t *); +/* Transaction cache support */ +extern void jbd2_journal_destroy_transaction_cache(void); +extern int jbd2_journal_init_transaction_cache(void); +extern void jbd2_journal_free_transaction(transaction_t *); + /* * Journal locking. * -- cgit v1.2.3 From e12bc14b88d44e5c1456dccb59ff58103f6c6edc Mon Sep 17 00:00:00 2001 From: Ohad Ben-Cohen Date: Tue, 31 Jan 2012 16:07:27 +0200 Subject: remoteproc: s/big switch/lookup table/ A lookup table would be easier to extend, and the resulting code is a bit cleaner. Reported-by: Grant Likely Signed-off-by: Ohad Ben-Cohen --- drivers/remoteproc/remoteproc_core.c | 45 ++++++++++++++++++------------------ include/linux/remoteproc.h | 7 ++++++ 2 files changed, 30 insertions(+), 22 deletions(-) (limited to 'include/linux') diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c index 567a3c59b4af..729911b67a9a 100644 --- a/drivers/remoteproc/remoteproc_core.c +++ b/drivers/remoteproc/remoteproc_core.c @@ -64,6 +64,8 @@ static DEFINE_KLIST(rprocs, klist_rproc_get, klist_rproc_put); typedef int (*rproc_handle_resources_t)(struct rproc *rproc, struct fw_resource *rsc, int len); +typedef int (*rproc_handle_resource_t)(struct rproc *rproc, + struct fw_resource *rsc); /* * This is the IOMMU fault handler we register with the IOMMU API @@ -658,44 +660,43 @@ free_mapping: return ret; } +/* + * A lookup table for resource handlers. The indices are defined in + * enum fw_resource_type. + */ +static rproc_handle_resource_t rproc_handle_rsc[] = { + [RSC_CARVEOUT] = rproc_handle_carveout, + [RSC_DEVMEM] = rproc_handle_devmem, + [RSC_TRACE] = rproc_handle_trace, + [RSC_VRING] = rproc_handle_vring, + [RSC_VIRTIO_DEV] = NULL, /* handled early upon registration */ +}; + /* handle firmware resource entries before booting the remote processor */ static int rproc_handle_boot_rsc(struct rproc *rproc, struct fw_resource *rsc, int len) { struct device *dev = rproc->dev; + rproc_handle_resource_t handler; int ret = 0; - while (len >= sizeof(*rsc)) { + for (; len >= sizeof(*rsc); rsc++, len -= sizeof(*rsc)) { dev_dbg(dev, "rsc: type %d, da 0x%llx, pa 0x%llx, len 0x%x, " "id %d, name %s, flags %x\n", rsc->type, rsc->da, rsc->pa, rsc->len, rsc->id, rsc->name, rsc->flags); - switch (rsc->type) { - case RSC_CARVEOUT: - ret = rproc_handle_carveout(rproc, rsc); - break; - case RSC_DEVMEM: - ret = rproc_handle_devmem(rproc, rsc); - break; - case RSC_TRACE: - ret = rproc_handle_trace(rproc, rsc); - break; - case RSC_VRING: - ret = rproc_handle_vring(rproc, rsc); - break; - case RSC_VIRTIO_DEV: - /* this one is handled early upon registration */ - break; - default: + if (rsc->type >= RSC_LAST) { dev_warn(dev, "unsupported resource %d\n", rsc->type); - break; + continue; } + handler = rproc_handle_rsc[rsc->type]; + if (!handler) + continue; + + ret = handler(rproc, rsc); if (ret) break; - - rsc++; - len -= sizeof(*rsc); } return ret; diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h index b52f78413c5c..ada4cb063dfe 100644 --- a/include/linux/remoteproc.h +++ b/include/linux/remoteproc.h @@ -103,6 +103,7 @@ struct fw_resource { * the virtio device features, 'pa' holds the virtio guest * features, 'len' holds the virtio status, and 'flags' holds * the virtio id (currently only VIRTIO_ID_RPMSG is supported). + * @RSC_LAST: just keep this one at the end * * Most of the resource entries share the basic idea of address/length * negotiation with the host: the firmware usually asks (on behalf of the @@ -115,6 +116,11 @@ struct fw_resource { * will contain the expected device addresses (today we actually only support * this scheme, as there aren't yet any use cases for dynamically allocated * device addresses). + * + * Please note that these values are used as indices to the rproc_handle_rsc + * lookup table, so please keep them sane. Moreover, @RSC_LAST is used to + * check the validity of an index before the lookup table is accessed, so + * please update it as needed. */ enum fw_resource_type { RSC_CARVEOUT = 0, @@ -122,6 +128,7 @@ enum fw_resource_type { RSC_TRACE = 2, RSC_VRING = 3, RSC_VIRTIO_DEV = 4, + RSC_LAST = 5, }; /** -- cgit v1.2.3 From 26b4950de174bc96c27b77546370dec84fb75ae7 Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Sat, 18 Feb 2012 22:03:14 +0100 Subject: firewire: core: prefix log messages with card name Associate all log messages from firewire-core with the respective card because some people have more than one card. E.g. firewire_ohci 0000:04:00.0: added OHCI v1.10 device as card 0, 8 IR + 8 IT contexts, quirks 0x0 firewire_ohci 0000:05:00.0: added OHCI v1.10 device as card 1, 8 IR + 8 IT contexts, quirks 0x0 firewire_core: created device fw0: GUID 0814438400000389, S800 firewire_core: phy config: new root=ffc1, gap_count=5 firewire_core: created device fw1: GUID 0814438400000388, S800 firewire_core: created device fw2: GUID 0001d202e06800d1, S800 turns into firewire_ohci 0000:04:00.0: added OHCI v1.10 device as card 0, 8 IR + 8 IT contexts, quirks 0x0 firewire_ohci 0000:05:00.0: added OHCI v1.10 device as card 1, 8 IR + 8 IT contexts, quirks 0x0 firewire_core 0000:04:00.0: created device fw0: GUID 0814438400000389, S800 firewire_core 0000:04:00.0: phy config: new root=ffc1, gap_count=5 firewire_core 0000:05:00.0: created device fw1: GUID 0814438400000388, S800 firewire_core 0000:04:00.0: created device fw2: GUID 0001d202e06800d1, S800 This increases the module size slightly; to keep this in check, turn the former printk wrapper macros into functions. Their implementation is largely copied from driver core's dev_printk counterparts. Signed-off-by: Stefan Richter --- drivers/firewire/core-card.c | 28 +++++++++++++---- drivers/firewire/core-cdev.c | 10 +++---- drivers/firewire/core-device.c | 60 +++++++++++++++++-------------------- drivers/firewire/core-topology.c | 18 +++++------ drivers/firewire/core-transaction.c | 4 +-- drivers/firewire/core.h | 6 ++++ include/linux/firewire.h | 3 -- 7 files changed, 72 insertions(+), 57 deletions(-) (limited to 'include/linux') diff --git a/drivers/firewire/core-card.c b/drivers/firewire/core-card.c index 85661b060ed7..b19db0f6a254 100644 --- a/drivers/firewire/core-card.c +++ b/drivers/firewire/core-card.c @@ -37,6 +37,22 @@ #include "core.h" +#define define_fw_printk_level(func, kern_level) \ +void func(const struct fw_card *card, const char *fmt, ...) \ +{ \ + struct va_format vaf; \ + va_list args; \ + \ + va_start(args, fmt); \ + vaf.fmt = fmt; \ + vaf.va = &args; \ + printk(kern_level KBUILD_MODNAME " %s: %pV", \ + dev_name(card->device), &vaf); \ + va_end(args); \ +} +define_fw_printk_level(fw_err, KERN_ERR); +define_fw_printk_level(fw_notice, KERN_NOTICE); + int fw_compute_block_crc(__be32 *block) { int length; @@ -260,7 +276,7 @@ static void allocate_broadcast_channel(struct fw_card *card, int generation) fw_iso_resource_manage(card, generation, 1ULL << 31, &channel, &bandwidth, true); if (channel != 31) { - fw_notify("failed to allocate broadcast channel\n"); + fw_notice(card, "failed to allocate broadcast channel\n"); return; } card->broadcast_channel_allocated = true; @@ -343,14 +359,14 @@ static void bm_work(struct work_struct *work) if (!card->irm_node->link_on) { new_root_id = local_id; - fw_notify("%s, making local node (%02x) root.\n", + fw_notice(card, "%s, making local node (%02x) root\n", "IRM has link off", new_root_id); goto pick_me; } if (irm_is_1394_1995_only && !keep_this_irm) { new_root_id = local_id; - fw_notify("%s, making local node (%02x) root.\n", + fw_notice(card, "%s, making local node (%02x) root\n", "IRM is not 1394a compliant", new_root_id); goto pick_me; } @@ -405,7 +421,7 @@ static void bm_work(struct work_struct *work) * root, and thus, IRM. */ new_root_id = local_id; - fw_notify("%s, making local node (%02x) root.\n", + fw_notice(card, "%s, making local node (%02x) root\n", "BM lock failed", new_root_id); goto pick_me; } @@ -478,8 +494,8 @@ static void bm_work(struct work_struct *work) spin_unlock_irq(&card->lock); if (do_reset) { - fw_notify("phy config: card %d, new root=%x, gap_count=%d\n", - card->index, new_root_id, gap_count); + fw_notice(card, "phy config: new root=%x, gap_count=%d\n", + new_root_id, gap_count); fw_send_phy_config(card, new_root_id, generation, gap_count); reset_bus(card, true); /* Will allocate broadcast channel after the reset. */ diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index 4799393247c8..f5f5a9706fcc 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -389,7 +389,7 @@ static void queue_bus_reset_event(struct client *client) e = kzalloc(sizeof(*e), GFP_KERNEL); if (e == NULL) { - fw_notify("Out of memory when allocating event\n"); + fw_notice(client->device->card, "out of memory when allocating event\n"); return; } @@ -691,7 +691,7 @@ static void handle_request(struct fw_card *card, struct fw_request *request, r = kmalloc(sizeof(*r), GFP_ATOMIC); e = kmalloc(sizeof(*e), GFP_ATOMIC); if (r == NULL || e == NULL) { - fw_notify("Out of memory when allocating event\n"); + fw_notice(card, "out of memory when allocating event\n"); goto failed; } r->card = card; @@ -928,7 +928,7 @@ static void iso_callback(struct fw_iso_context *context, u32 cycle, e = kmalloc(sizeof(*e) + header_length, GFP_ATOMIC); if (e == NULL) { - fw_notify("Out of memory when allocating event\n"); + fw_notice(context->card, "out of memory when allocating event\n"); return; } e->interrupt.type = FW_CDEV_EVENT_ISO_INTERRUPT; @@ -948,7 +948,7 @@ static void iso_mc_callback(struct fw_iso_context *context, e = kmalloc(sizeof(*e), GFP_ATOMIC); if (e == NULL) { - fw_notify("Out of memory when allocating event\n"); + fw_notice(context->card, "out of memory when allocating event\n"); return; } e->interrupt.type = FW_CDEV_EVENT_ISO_INTERRUPT_MULTICHANNEL; @@ -1548,7 +1548,7 @@ void fw_cdev_handle_phy_packet(struct fw_card *card, struct fw_packet *p) list_for_each_entry(client, &card->phy_receiver_list, phy_receiver_link) { e = kmalloc(sizeof(*e) + 8, GFP_ATOMIC); if (e == NULL) { - fw_notify("Out of memory when allocating event\n"); + fw_notice(card, "out of memory when allocating event\n"); break; } e->phy_packet.closure = client->phy_receiver_closure; diff --git a/drivers/firewire/core-device.c b/drivers/firewire/core-device.c index 4c6c7d8cdaf1..afa7c83bd114 100644 --- a/drivers/firewire/core-device.c +++ b/drivers/firewire/core-device.c @@ -485,6 +485,7 @@ static int read_rom(struct fw_device *device, */ static int read_config_rom(struct fw_device *device, int generation) { + struct fw_card *card = device->card; const u32 *old_rom, *new_rom; u32 *rom, *stack; u32 sp, key; @@ -529,12 +530,12 @@ static int read_config_rom(struct fw_device *device, int generation) */ if ((rom[2] & 0x7) < device->max_speed || device->max_speed == SCODE_BETA || - device->card->beta_repeaters_present) { + card->beta_repeaters_present) { u32 dummy; /* for S1600 and S3200 */ if (device->max_speed == SCODE_BETA) - device->max_speed = device->card->link_speed; + device->max_speed = card->link_speed; while (device->max_speed > SCODE_100) { if (read_rom(device, generation, 0, &dummy) == @@ -576,9 +577,9 @@ static int read_config_rom(struct fw_device *device, int generation) * a firmware bug. Ignore this whole block, i.e. * simply set a fake block length of 0. */ - fw_error("skipped invalid ROM block %x at %llx\n", - rom[i], - i * 4 | CSR_REGISTER_BASE | CSR_CONFIG_ROM); + fw_err(card, "skipped invalid ROM block %x at %llx\n", + rom[i], + i * 4 | CSR_REGISTER_BASE | CSR_CONFIG_ROM); rom[i] = 0; end = i; } @@ -604,9 +605,10 @@ static int read_config_rom(struct fw_device *device, int generation) * the ROM don't have to check offsets all the time. */ if (i + (rom[i] & 0xffffff) >= MAX_CONFIG_ROM_SIZE) { - fw_error("skipped unsupported ROM entry %x at %llx\n", - rom[i], - i * 4 | CSR_REGISTER_BASE | CSR_CONFIG_ROM); + fw_err(card, + "skipped unsupported ROM entry %x at %llx\n", + rom[i], + i * 4 | CSR_REGISTER_BASE | CSR_CONFIG_ROM); rom[i] = 0; continue; } @@ -673,7 +675,7 @@ static void create_units(struct fw_device *device) */ unit = kzalloc(sizeof(*unit), GFP_KERNEL); if (unit == NULL) { - fw_error("failed to allocate memory for unit\n"); + fw_err(device->card, "out of memory for unit\n"); continue; } @@ -875,7 +877,7 @@ static int lookup_existing_device(struct device *dev, void *data) smp_wmb(); /* update node_id before generation */ old->generation = card->generation; old->config_rom_retries = 0; - fw_notify("rediscovered device %s\n", dev_name(dev)); + fw_notice(card, "rediscovered device %s\n", dev_name(dev)); PREPARE_DELAYED_WORK(&old->work, fw_device_update); fw_schedule_device_work(old, 0); @@ -956,6 +958,7 @@ static void fw_device_init(struct work_struct *work) { struct fw_device *device = container_of(work, struct fw_device, work.work); + struct fw_card *card = device->card; struct device *revived_dev; int minor, ret; @@ -972,16 +975,16 @@ static void fw_device_init(struct work_struct *work) fw_schedule_device_work(device, RETRY_DELAY); } else { if (device->node->link_on) - fw_notify("giving up on config rom for node id %x\n", + fw_notice(card, "giving up on Config ROM for node id %x\n", device->node_id); - if (device->node == device->card->root_node) - fw_schedule_bm_work(device->card, 0); + if (device->node == card->root_node) + fw_schedule_bm_work(card, 0); fw_device_release(&device->device); } return; } - revived_dev = device_find_child(device->card->device, + revived_dev = device_find_child(card->device, device, lookup_existing_device); if (revived_dev) { put_device(revived_dev); @@ -1004,7 +1007,7 @@ static void fw_device_init(struct work_struct *work) device->device.bus = &fw_bus_type; device->device.type = &fw_device_type; - device->device.parent = device->card->device; + device->device.parent = card->device; device->device.devt = MKDEV(fw_cdev_major, minor); dev_set_name(&device->device, "fw%d", minor); @@ -1016,7 +1019,7 @@ static void fw_device_init(struct work_struct *work) &device->attribute_group); if (device_add(&device->device)) { - fw_error("Failed to add device.\n"); + fw_err(card, "failed to add device\n"); goto error_with_cdev; } @@ -1037,18 +1040,10 @@ static void fw_device_init(struct work_struct *work) PREPARE_DELAYED_WORK(&device->work, fw_device_shutdown); fw_schedule_device_work(device, SHUTDOWN_DELAY); } else { - if (device->config_rom_retries) - fw_notify("created device %s: GUID %08x%08x, S%d00, " - "%d config ROM retries\n", - dev_name(&device->device), - device->config_rom[3], device->config_rom[4], - 1 << device->max_speed, - device->config_rom_retries); - else - fw_notify("created device %s: GUID %08x%08x, S%d00\n", - dev_name(&device->device), - device->config_rom[3], device->config_rom[4], - 1 << device->max_speed); + fw_notice(card, "created device %s: GUID %08x%08x, S%d00\n", + dev_name(&device->device), + device->config_rom[3], device->config_rom[4], + 1 << device->max_speed); device->config_rom_retries = 0; set_broadcast_channel(device, device->generation); @@ -1060,8 +1055,8 @@ static void fw_device_init(struct work_struct *work) * just end up running the IRM work a couple of extra times - * pretty harmless. */ - if (device->node == device->card->root_node) - fw_schedule_bm_work(device->card, 0); + if (device->node == card->root_node) + fw_schedule_bm_work(card, 0); return; @@ -1165,12 +1160,13 @@ static void fw_device_refresh(struct work_struct *work) FW_DEVICE_RUNNING) == FW_DEVICE_GONE) goto gone; - fw_notify("refreshed device %s\n", dev_name(&device->device)); + fw_notice(card, "refreshed device %s\n", dev_name(&device->device)); device->config_rom_retries = 0; goto out; give_up: - fw_notify("giving up on refresh of device %s\n", dev_name(&device->device)); + fw_notice(card, "giving up on refresh of device %s\n", + dev_name(&device->device)); gone: atomic_set(&device->state, FW_DEVICE_GONE); PREPARE_DELAYED_WORK(&device->work, fw_device_shutdown); diff --git a/drivers/firewire/core-topology.c b/drivers/firewire/core-topology.c index 94d3b494ddfb..75a6b0df670a 100644 --- a/drivers/firewire/core-topology.c +++ b/drivers/firewire/core-topology.c @@ -205,19 +205,19 @@ static struct fw_node *build_tree(struct fw_card *card, next_sid = count_ports(sid, &port_count, &child_port_count); if (next_sid == NULL) { - fw_error("Inconsistent extended self IDs.\n"); + fw_err(card, "inconsistent extended self IDs\n"); return NULL; } q = *sid; if (phy_id != SELF_ID_PHY_ID(q)) { - fw_error("PHY ID mismatch in self ID: %d != %d.\n", - phy_id, SELF_ID_PHY_ID(q)); + fw_err(card, "PHY ID mismatch in self ID: %d != %d\n", + phy_id, SELF_ID_PHY_ID(q)); return NULL; } if (child_port_count > stack_depth) { - fw_error("Topology stack underflow\n"); + fw_err(card, "topology stack underflow\n"); return NULL; } @@ -235,7 +235,7 @@ static struct fw_node *build_tree(struct fw_card *card, node = fw_node_create(q, port_count, card->color); if (node == NULL) { - fw_error("Out of memory while building topology.\n"); + fw_err(card, "out of memory while building topology\n"); return NULL; } @@ -284,8 +284,8 @@ static struct fw_node *build_tree(struct fw_card *card, */ if ((next_sid == end && parent_count != 0) || (next_sid < end && parent_count != 1)) { - fw_error("Parent port inconsistency for node %d: " - "parent_count=%d\n", phy_id, parent_count); + fw_err(card, "parent port inconsistency for node %d: " + "parent_count=%d\n", phy_id, parent_count); return NULL; } @@ -530,7 +530,7 @@ void fw_core_handle_bus_reset(struct fw_card *card, int node_id, int generation, */ if (!is_next_generation(generation, card->generation) && card->local_node != NULL) { - fw_notify("skipped bus generations, destroying all nodes\n"); + fw_notice(card, "skipped bus generations, destroying all nodes\n"); fw_destroy_nodes(card); card->bm_retries = 0; } @@ -557,7 +557,7 @@ void fw_core_handle_bus_reset(struct fw_card *card, int node_id, int generation, card->color++; if (local_node == NULL) { - fw_error("topology build failed\n"); + fw_err(card, "topology build failed\n"); /* FIXME: We need to issue a bus reset in this case. */ } else if (card->local_node == NULL) { card->local_node = local_node; diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c index 855ab3f5936f..426886a91bd1 100644 --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c @@ -770,7 +770,7 @@ static struct fw_request *allocate_request(struct fw_card *card, break; default: - fw_error("ERROR - corrupt request received - %08x %08x %08x\n", + fw_notice(card, "ERROR - corrupt request received - %08x %08x %08x\n", p->header[0], p->header[1], p->header[2]); return NULL; } @@ -960,7 +960,7 @@ void fw_core_handle_response(struct fw_card *card, struct fw_packet *p) if (&t->link == &card->transaction_list) { timed_out: - fw_notify("Unsolicited response (source %x, tlabel %x)\n", + fw_notice(card, "unsolicited response (source %x, tlabel %x)\n", source, tlabel); return; } diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h index b5b34952cf16..62f57a4331e3 100644 --- a/drivers/firewire/core.h +++ b/drivers/firewire/core.h @@ -1,6 +1,7 @@ #ifndef _FIREWIRE_CORE_H #define _FIREWIRE_CORE_H +#include #include #include #include @@ -24,6 +25,11 @@ struct fw_packet; /* -card */ +extern __printf(2, 3) +void fw_err(const struct fw_card *card, const char *fmt, ...); +extern __printf(2, 3) +void fw_notice(const struct fw_card *card, const char *fmt, ...); + /* bitfields within the PHY registers */ #define PHY_LINK_ACTIVE 0x80 #define PHY_CONTENDER 0x40 diff --git a/include/linux/firewire.h b/include/linux/firewire.h index 6f1d7385e051..ab5b7a18decf 100644 --- a/include/linux/firewire.h +++ b/include/linux/firewire.h @@ -17,9 +17,6 @@ #include #include -#define fw_notify(s, args...) printk(KERN_NOTICE KBUILD_MODNAME ": " s, ## args) -#define fw_error(s, args...) printk(KERN_ERR KBUILD_MODNAME ": " s, ## args) - #define CSR_REGISTER_BASE 0xfffff0000000ULL /* register offsets are relative to CSR_REGISTER_BASE */ -- cgit v1.2.3 From 6651b0ea9202541091f6c23ec8715122ea8cc1b0 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Tue, 20 Sep 2011 12:23:58 +0300 Subject: OAMPFB: remove unused omapfb_set_ctrl_platform_data() omapfb_set_ctrl_platform_data() is no longer used, so it can be removed. Signed-off-by: Tomi Valkeinen Acked-by: Tony Lindgren --- arch/arm/plat-omap/fb.c | 5 ----- include/linux/omapfb.h | 2 -- 2 files changed, 7 deletions(-) (limited to 'include/linux') diff --git a/arch/arm/plat-omap/fb.c b/arch/arm/plat-omap/fb.c index 15d4d1657b17..a05ab5673556 100644 --- a/arch/arm/plat-omap/fb.c +++ b/arch/arm/plat-omap/fb.c @@ -175,11 +175,6 @@ void __init omapfb_reserve_sdram_memblock(void) reserved); } -void omapfb_set_ctrl_platform_data(void *data) -{ - omapfb_config.ctrl_platform_data = data; -} - static int __init omap_init_fb(void) { const struct omap_lcd_config *conf; diff --git a/include/linux/omapfb.h b/include/linux/omapfb.h index c0b018790f07..85cad31ca3d9 100644 --- a/include/linux/omapfb.h +++ b/include/linux/omapfb.h @@ -250,12 +250,10 @@ struct omapfb_mem_desc { struct omapfb_platform_data { struct omap_lcd_config lcd; struct omapfb_mem_desc mem_desc; - void *ctrl_platform_data; }; /* in arch/arm/plat-omap/fb.c */ extern void omapfb_set_platform_data(struct omapfb_platform_data *data); -extern void omapfb_set_ctrl_platform_data(void *pdata); extern void omapfb_reserve_sdram_memblock(void); #endif -- cgit v1.2.3 From 1e434f9318efc3dddc0c0b8d2071712668154c2b Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Tue, 20 Sep 2011 14:12:43 +0300 Subject: OMAPFB: remove early mem alloc from old omapfb arch/arm/plat-omap/fb.c contains code to alloc omapfb buffers at early boot time according to information given from the bootloader or board file. This code isn't currently used by any board, and is anyway something that the newer vram.c could handle. So remove the alloc code and in later patches make old omapfb driver use vram.c. Signed-off-by: Tomi Valkeinen Acked-by: Tony Lindgren --- arch/arm/plat-omap/common.c | 2 - arch/arm/plat-omap/fb.c | 140 +------------------------------------------- include/linux/omapfb.h | 1 - 3 files changed, 2 insertions(+), 141 deletions(-) (limited to 'include/linux') diff --git a/arch/arm/plat-omap/common.c b/arch/arm/plat-omap/common.c index 06383b51e655..2081f0f578e5 100644 --- a/arch/arm/plat-omap/common.c +++ b/arch/arm/plat-omap/common.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include @@ -65,7 +64,6 @@ const void *__init omap_get_var_config(u16 tag, size_t *len) void __init omap_reserve(void) { - omapfb_reserve_sdram_memblock(); omap_vram_reserve_sdram_memblock(); omap_dsp_reserve_sdram_memblock(); omap_secure_ram_reserve_memblock(); diff --git a/arch/arm/plat-omap/fb.c b/arch/arm/plat-omap/fb.c index a05ab5673556..a742b60912cd 100644 --- a/arch/arm/plat-omap/fb.c +++ b/arch/arm/plat-omap/fb.c @@ -38,8 +38,6 @@ #if defined(CONFIG_FB_OMAP) || defined(CONFIG_FB_OMAP_MODULE) static struct omapfb_platform_data omapfb_config; -static int config_invalid; -static int configured_regions; static u64 omap_fb_dma_mask = ~(u32)0; @@ -58,140 +56,14 @@ void omapfb_set_platform_data(struct omapfb_platform_data *data) { } -static inline int ranges_overlap(unsigned long start1, unsigned long size1, - unsigned long start2, unsigned long size2) -{ - return (start1 >= start2 && start1 < start2 + size2) || - (start2 >= start1 && start2 < start1 + size1); -} - -static inline int range_included(unsigned long start1, unsigned long size1, - unsigned long start2, unsigned long size2) -{ - return start1 >= start2 && start1 + size1 <= start2 + size2; -} - -/* - * Get the region_idx`th region from board config/ATAG and convert it to - * our internal format. - */ -static int __init get_fbmem_region(int region_idx, struct omapfb_mem_region *rg) -{ - const struct omap_fbmem_config *conf; - u32 paddr; - - conf = omap_get_nr_config(OMAP_TAG_FBMEM, - struct omap_fbmem_config, region_idx); - if (conf == NULL) - return -ENOENT; - - paddr = conf->start; - /* - * Low bits encode the page allocation mode, if high bits - * are zero. Otherwise we need a page aligned fixed - * address. - */ - memset(rg, 0, sizeof(*rg)); - rg->type = paddr & ~PAGE_MASK; - rg->paddr = paddr & PAGE_MASK; - rg->size = PAGE_ALIGN(conf->size); - return 0; -} - -static int valid_sdram(unsigned long addr, unsigned long size) -{ - return memblock_is_region_memory(addr, size); -} - -static int reserve_sdram(unsigned long addr, unsigned long size) -{ - if (memblock_is_region_reserved(addr, size)) - return -EBUSY; - if (memblock_reserve(addr, size)) - return -ENOMEM; - return 0; -} - -/* - * Called from map_io. We need to call to this early enough so that we - * can reserve the fixed SDRAM regions before VM could get hold of them. - */ -void __init omapfb_reserve_sdram_memblock(void) -{ - unsigned long reserved = 0; - int i; - - if (config_invalid) - return; - - for (i = 0; ; i++) { - struct omapfb_mem_region rg; - - if (get_fbmem_region(i, &rg) < 0) - break; - - if (i == OMAPFB_PLANE_NUM) { - pr_err("Extraneous FB mem configuration entries\n"); - config_invalid = 1; - return; - } - - /* Check if it's our memory type. */ - if (rg.type != OMAPFB_MEMTYPE_SDRAM) - continue; - - /* Check if the region falls within SDRAM */ - if (rg.paddr && !valid_sdram(rg.paddr, rg.size)) - continue; - - if (rg.size == 0) { - pr_err("Zero size for FB region %d\n", i); - config_invalid = 1; - return; - } - - if (rg.paddr) { - if (reserve_sdram(rg.paddr, rg.size)) { - pr_err("Trying to use reserved memory for FB region %d\n", - i); - config_invalid = 1; - return; - } - reserved += rg.size; - } - - if (omapfb_config.mem_desc.region[i].size) { - pr_err("FB region %d already set\n", i); - config_invalid = 1; - return; - } - - omapfb_config.mem_desc.region[i] = rg; - configured_regions++; - } - omapfb_config.mem_desc.region_cnt = i; - if (reserved) - pr_info("Reserving %lu bytes SDRAM for frame buffer\n", - reserved); -} - static int __init omap_init_fb(void) { const struct omap_lcd_config *conf; - if (config_invalid) - return 0; - if (configured_regions != omapfb_config.mem_desc.region_cnt) { - printk(KERN_ERR "Invalid FB mem configuration entries\n"); - return 0; - } conf = omap_get_config(OMAP_TAG_LCD, struct omap_lcd_config); - if (conf == NULL) { - if (configured_regions) - /* FB mem config, but no LCD config? */ - printk(KERN_ERR "Missing LCD configuration\n"); + if (conf == NULL) return 0; - } + omapfb_config.lcd = *conf; return platform_device_register(&omap_fb_device); @@ -227,18 +99,10 @@ static int __init omap_init_fb(void) arch_initcall(omap_init_fb); -void omapfb_reserve_sdram_memblock(void) -{ -} - #else void omapfb_set_platform_data(struct omapfb_platform_data *data) { } -void omapfb_reserve_sdram_memblock(void) -{ -} - #endif diff --git a/include/linux/omapfb.h b/include/linux/omapfb.h index 85cad31ca3d9..7ab9cebf9164 100644 --- a/include/linux/omapfb.h +++ b/include/linux/omapfb.h @@ -254,7 +254,6 @@ struct omapfb_platform_data { /* in arch/arm/plat-omap/fb.c */ extern void omapfb_set_platform_data(struct omapfb_platform_data *data); -extern void omapfb_reserve_sdram_memblock(void); #endif -- cgit v1.2.3 From 80277566d0d85b3430548ba87ad28b0585ef06a6 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Tue, 20 Sep 2011 14:27:44 +0300 Subject: OMAPFB: remove mem info from platform_data omapfb driver used platform_data to get fb memory areas and formats defined by the board file. This patch removes omapfb's (both old and new omapfb) use of the memory data in platform_data, because: - No board uses them currently - It's not board file's job to define things like amount of default framebuffer memory. These should come from the bootloader via command line parameters. Signed-off-by: Tomi Valkeinen --- drivers/video/omap/omapfb_main.c | 5 ---- drivers/video/omap2/omapfb/omapfb-main.c | 43 -------------------------------- include/linux/omapfb.h | 1 - 3 files changed, 49 deletions(-) (limited to 'include/linux') diff --git a/drivers/video/omap/omapfb_main.c b/drivers/video/omap/omapfb_main.c index aaf0990162cc..3d2e14798210 100644 --- a/drivers/video/omap/omapfb_main.c +++ b/drivers/video/omap/omapfb_main.c @@ -157,11 +157,6 @@ static int ctrl_init(struct omapfb_device *fbdev) fbdev->mem_desc.region[i].size = PAGE_ALIGN(def_vram[i]); fbdev->mem_desc.region_cnt = i; - } else { - struct omapfb_platform_data *conf; - - conf = fbdev->dev->platform_data; - fbdev->mem_desc = conf->mem_desc; } if (!fbdev->mem_desc.region_cnt) { diff --git a/drivers/video/omap2/omapfb/omapfb-main.c b/drivers/video/omap2/omapfb/omapfb-main.c index 17e7320b48d5..4c28d57e34df 100644 --- a/drivers/video/omap2/omapfb/omapfb-main.c +++ b/drivers/video/omap2/omapfb/omapfb-main.c @@ -1614,23 +1614,6 @@ static int omapfb_allocate_all_fbs(struct omapfb2_device *fbdev) memset(&vram_paddrs, 0, sizeof(vram_paddrs)); } - if (fbdev->dev->platform_data) { - struct omapfb_platform_data *opd; - opd = fbdev->dev->platform_data; - for (i = 0; i < opd->mem_desc.region_cnt; ++i) { - if (!vram_sizes[i]) { - unsigned long size; - unsigned long paddr; - - size = opd->mem_desc.region[i].size; - paddr = opd->mem_desc.region[i].paddr; - - vram_sizes[i] = size; - vram_paddrs[i] = paddr; - } - } - } - for (i = 0; i < fbdev->num_fbs; i++) { /* allocate memory automatically only for fb0, or if * excplicitly defined with vram or plat data option */ @@ -1828,32 +1811,6 @@ static int omapfb_fb_init(struct omapfb2_device *fbdev, struct fb_info *fbi) var->rotate = def_rotate; - /* - * Check if there is a default color format set in the board file, - * and use this format instead the default deducted from the - * display bpp. - */ - if (fbdev->dev->platform_data) { - struct omapfb_platform_data *opd; - int id = ofbi->id; - - opd = fbdev->dev->platform_data; - if (opd->mem_desc.region[id].format_used) { - enum omap_color_mode mode; - enum omapfb_color_format format; - - format = opd->mem_desc.region[id].format; - mode = fb_format_to_dss_mode(format); - if (mode < 0) { - r = mode; - goto err; - } - r = dss_mode_to_fb_mode(mode, var); - if (r < 0) - goto err; - } - } - if (display) { u16 w, h; int rotation = (var->rotate + ofbi->rotation[0]) % 4; diff --git a/include/linux/omapfb.h b/include/linux/omapfb.h index 7ab9cebf9164..03f52555ce63 100644 --- a/include/linux/omapfb.h +++ b/include/linux/omapfb.h @@ -249,7 +249,6 @@ struct omapfb_mem_desc { struct omapfb_platform_data { struct omap_lcd_config lcd; - struct omapfb_mem_desc mem_desc; }; /* in arch/arm/plat-omap/fb.c */ -- cgit v1.2.3 From 8f5e35a7942afe70eb0ba0d8193801b7989ae03a Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Tue, 20 Sep 2011 14:43:14 +0300 Subject: OMAPFB: Move old omapfb private structs to a private include file include/linux/omapfb.h contains structs that are used only by the omapfb driver. Move the structs into drivers/video/omap/omapfb.h. Signed-off-by: Tomi Valkeinen --- drivers/video/omap/omapfb.h | 25 +++++++++++++++++++++++++ include/linux/omapfb.h | 25 ------------------------- 2 files changed, 25 insertions(+), 25 deletions(-) (limited to 'include/linux') diff --git a/drivers/video/omap/omapfb.h b/drivers/video/omap/omapfb.h index 3a5ab13845fc..07fe49878892 100644 --- a/drivers/video/omap/omapfb.h +++ b/drivers/video/omap/omapfb.h @@ -47,6 +47,31 @@ struct omapfb_device; +#ifdef CONFIG_ARCH_OMAP1 +#define OMAPFB_PLANE_NUM 1 +#else +#define OMAPFB_PLANE_NUM 3 +#endif + +struct omapfb_mem_region { + u32 paddr; + void __iomem *vaddr; + unsigned long size; + u8 type; /* OMAPFB_PLANE_MEM_* */ + enum omapfb_color_format format;/* OMAPFB_COLOR_* */ + unsigned format_used:1; /* Must be set when format is set. + * Needed b/c of the badly chosen 0 + * base for OMAPFB_COLOR_* values + */ + unsigned alloc:1; /* allocated by the driver */ + unsigned map:1; /* kernel mapped by the driver */ +}; + +struct omapfb_mem_desc { + int region_cnt; + struct omapfb_mem_region region[OMAPFB_PLANE_NUM]; +}; + struct lcd_panel { const char *name; int config; /* TFT/STN, signal inversion */ diff --git a/include/linux/omapfb.h b/include/linux/omapfb.h index 03f52555ce63..7a8e0ea345eb 100644 --- a/include/linux/omapfb.h +++ b/include/linux/omapfb.h @@ -222,31 +222,6 @@ struct omapfb_display_info { #include -#ifdef CONFIG_ARCH_OMAP1 -#define OMAPFB_PLANE_NUM 1 -#else -#define OMAPFB_PLANE_NUM 3 -#endif - -struct omapfb_mem_region { - u32 paddr; - void __iomem *vaddr; - unsigned long size; - u8 type; /* OMAPFB_PLANE_MEM_* */ - enum omapfb_color_format format;/* OMAPFB_COLOR_* */ - unsigned format_used:1; /* Must be set when format is set. - * Needed b/c of the badly chosen 0 - * base for OMAPFB_COLOR_* values - */ - unsigned alloc:1; /* allocated by the driver */ - unsigned map:1; /* kernel mapped by the driver */ -}; - -struct omapfb_mem_desc { - int region_cnt; - struct omapfb_mem_region region[OMAPFB_PLANE_NUM]; -}; - struct omapfb_platform_data { struct omap_lcd_config lcd; }; -- cgit v1.2.3 From f060f95365ce71acbf29ef5dac580ab067600f4c Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Tue, 20 Sep 2011 14:49:50 +0300 Subject: OMAPFB: remove omapfb_set_platform_data() omapfb_set_platform_data() is no longer used, so remove it. Signed-off-by: Tomi Valkeinen Acked-by: Tony Lindgren --- arch/arm/plat-omap/fb.c | 15 --------------- include/linux/omapfb.h | 3 --- 2 files changed, 18 deletions(-) (limited to 'include/linux') diff --git a/arch/arm/plat-omap/fb.c b/arch/arm/plat-omap/fb.c index a742b60912cd..45f35c983f44 100644 --- a/arch/arm/plat-omap/fb.c +++ b/arch/arm/plat-omap/fb.c @@ -52,10 +52,6 @@ static struct platform_device omap_fb_device = { .num_resources = 0, }; -void omapfb_set_platform_data(struct omapfb_platform_data *data) -{ -} - static int __init omap_init_fb(void) { const struct omap_lcd_config *conf; @@ -87,11 +83,6 @@ static struct platform_device omap_fb_device = { .num_resources = 0, }; -void omapfb_set_platform_data(struct omapfb_platform_data *data) -{ - omapfb_config = *data; -} - static int __init omap_init_fb(void) { return platform_device_register(&omap_fb_device); @@ -99,10 +90,4 @@ static int __init omap_init_fb(void) arch_initcall(omap_init_fb); -#else - -void omapfb_set_platform_data(struct omapfb_platform_data *data) -{ -} - #endif diff --git a/include/linux/omapfb.h b/include/linux/omapfb.h index 7a8e0ea345eb..a575706a83c3 100644 --- a/include/linux/omapfb.h +++ b/include/linux/omapfb.h @@ -226,9 +226,6 @@ struct omapfb_platform_data { struct omap_lcd_config lcd; }; -/* in arch/arm/plat-omap/fb.c */ -extern void omapfb_set_platform_data(struct omapfb_platform_data *data); - #endif #endif /* __OMAPFB_H */ -- cgit v1.2.3 From ddba6c7f7ec6a82ccbce4126d615e73e00b4be12 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Tue, 20 Sep 2011 15:23:13 +0300 Subject: OMAP1: pass LCD config with omapfb_set_lcd_config() LCD config for old omapfb driver is passed with OMAP_TAG_LCD from board files or from the bootloader. In an effort to remove OMAP_TAG_LCD, this patch adds omapfb_set_lcd_config() function that the board files can call to set the LCD config. This has the drawback that configuration can no longer come from the bootloader. Of the boards supported by the kernel, this should only affect N770 which depends on the data from the bootloader. This patch adds an LCD config for N770 to its board files, but that is most probably broken. Fixing this would need information about the HW setup in N770 boards. Signed-off-by: Tomi Valkeinen Acked-by: Tony Lindgren --- arch/arm/mach-omap1/board-ams-delta.c | 9 +++------ arch/arm/mach-omap1/board-fsample.c | 9 +++------ arch/arm/mach-omap1/board-h2.c | 9 +++------ arch/arm/mach-omap1/board-h3.c | 9 +++------ arch/arm/mach-omap1/board-htcherald.c | 9 +++------ arch/arm/mach-omap1/board-innovator.c | 11 +++-------- arch/arm/mach-omap1/board-nokia770.c | 13 +++++++------ arch/arm/mach-omap1/board-osk.c | 14 ++++++-------- arch/arm/mach-omap1/board-palmte.c | 10 +++------- arch/arm/mach-omap1/board-palmtt.c | 10 +++------- arch/arm/mach-omap1/board-palmz71.c | 10 +++------- arch/arm/mach-omap1/board-perseus2.c | 9 +++------ arch/arm/mach-omap1/board-sx1.c | 10 +++------- arch/arm/plat-omap/fb.c | 22 ++++++++++++++++------ include/linux/omapfb.h | 2 ++ 15 files changed, 64 insertions(+), 92 deletions(-) (limited to 'include/linux') diff --git a/arch/arm/mach-omap1/board-ams-delta.c b/arch/arm/mach-omap1/board-ams-delta.c index 88909cc0b254..e0e8245f3c9f 100644 --- a/arch/arm/mach-omap1/board-ams-delta.c +++ b/arch/arm/mach-omap1/board-ams-delta.c @@ -20,6 +20,7 @@ #include #include #include +#include #include @@ -169,10 +170,6 @@ static struct omap_usb_config ams_delta_usb_config __initdata = { .pins[0] = 2, }; -static struct omap_board_config_kernel ams_delta_config[] __initdata = { - { OMAP_TAG_LCD, &ams_delta_lcd_config }, -}; - static struct resource ams_delta_nand_resources[] = { [0] = { .start = OMAP1_MPUIO_BASE, @@ -302,8 +299,6 @@ static void __init ams_delta_init(void) omap_cfg_reg(J19_1610_CAM_D6); omap_cfg_reg(J18_1610_CAM_D7); - omap_board_config = ams_delta_config; - omap_board_config_size = ARRAY_SIZE(ams_delta_config); omap_serial_init(); omap_register_i2c_bus(1, 100, NULL, 0); @@ -321,6 +316,8 @@ static void __init ams_delta_init(void) ams_delta_init_fiq(); omap_writew(omap_readw(ARM_RSTCT1) | 0x0004, ARM_RSTCT1); + + omapfb_set_lcd_config(&ams_delta_lcd_config); } static struct plat_serial8250_port ams_delta_modem_ports[] = { diff --git a/arch/arm/mach-omap1/board-fsample.c b/arch/arm/mach-omap1/board-fsample.c index 0b9464b41212..b00844f7414d 100644 --- a/arch/arm/mach-omap1/board-fsample.c +++ b/arch/arm/mach-omap1/board-fsample.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -290,10 +291,6 @@ static struct omap_lcd_config fsample_lcd_config = { .ctrl_name = "internal", }; -static struct omap_board_config_kernel fsample_config[] __initdata = { - { OMAP_TAG_LCD, &fsample_lcd_config }, -}; - static void __init omap_fsample_init(void) { /* Early, board-dependent init */ @@ -352,10 +349,10 @@ static void __init omap_fsample_init(void) platform_add_devices(devices, ARRAY_SIZE(devices)); - omap_board_config = fsample_config; - omap_board_config_size = ARRAY_SIZE(fsample_config); omap_serial_init(); omap_register_i2c_bus(1, 100, NULL, 0); + + omapfb_set_lcd_config(&fsample_lcd_config); } /* Only FPGA needs to be mapped here. All others are done with ioremap */ diff --git a/arch/arm/mach-omap1/board-h2.c b/arch/arm/mach-omap1/board-h2.c index 00ad6b22d60a..471233fe147a 100644 --- a/arch/arm/mach-omap1/board-h2.c +++ b/arch/arm/mach-omap1/board-h2.c @@ -30,6 +30,7 @@ #include #include #include +#include #include @@ -391,10 +392,6 @@ static struct omap_lcd_config h2_lcd_config __initdata = { .ctrl_name = "internal", }; -static struct omap_board_config_kernel h2_config[] __initdata = { - { OMAP_TAG_LCD, &h2_lcd_config }, -}; - static void __init h2_init(void) { h2_init_smc91x(); @@ -438,13 +435,13 @@ static void __init h2_init(void) omap_cfg_reg(N19_1610_KBR5); platform_add_devices(h2_devices, ARRAY_SIZE(h2_devices)); - omap_board_config = h2_config; - omap_board_config_size = ARRAY_SIZE(h2_config); omap_serial_init(); omap_register_i2c_bus(1, 100, h2_i2c_board_info, ARRAY_SIZE(h2_i2c_board_info)); omap1_usb_init(&h2_usb_config); h2_mmc_init(); + + omapfb_set_lcd_config(&h2_lcd_config); } MACHINE_START(OMAP_H2, "TI-H2") diff --git a/arch/arm/mach-omap1/board-h3.c b/arch/arm/mach-omap1/board-h3.c index 4a7f25149703..7cfd25b90735 100644 --- a/arch/arm/mach-omap1/board-h3.c +++ b/arch/arm/mach-omap1/board-h3.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -370,10 +371,6 @@ static struct omap_lcd_config h3_lcd_config __initdata = { .ctrl_name = "internal", }; -static struct omap_board_config_kernel h3_config[] __initdata = { - { OMAP_TAG_LCD, &h3_lcd_config }, -}; - static struct i2c_board_info __initdata h3_i2c_board_info[] = { { I2C_BOARD_INFO("tps65013", 0x48), @@ -426,13 +423,13 @@ static void __init h3_init(void) platform_add_devices(devices, ARRAY_SIZE(devices)); spi_register_board_info(h3_spi_board_info, ARRAY_SIZE(h3_spi_board_info)); - omap_board_config = h3_config; - omap_board_config_size = ARRAY_SIZE(h3_config); omap_serial_init(); omap_register_i2c_bus(1, 100, h3_i2c_board_info, ARRAY_SIZE(h3_i2c_board_info)); omap1_usb_init(&h3_usb_config); h3_mmc_init(); + + omapfb_set_lcd_config(&h3_lcd_config); } MACHINE_START(OMAP_H3, "TI OMAP1710 H3 board") diff --git a/arch/arm/mach-omap1/board-htcherald.c b/arch/arm/mach-omap1/board-htcherald.c index 731cc3db7ab3..af2afcf24f75 100644 --- a/arch/arm/mach-omap1/board-htcherald.c +++ b/arch/arm/mach-omap1/board-htcherald.c @@ -36,6 +36,7 @@ #include #include #include +#include #include #include @@ -398,10 +399,6 @@ static struct omap_lcd_config htcherald_lcd_config __initdata = { .ctrl_name = "internal", }; -static struct omap_board_config_kernel htcherald_config[] __initdata = { - { OMAP_TAG_LCD, &htcherald_lcd_config }, -}; - static struct platform_device lcd_device = { .name = "lcd_htcherald", .id = -1, @@ -580,8 +577,6 @@ static void __init htcherald_init(void) printk(KERN_INFO "HTC Herald init.\n"); /* Do board initialization before we register all the devices */ - omap_board_config = htcherald_config; - omap_board_config_size = ARRAY_SIZE(htcherald_config); platform_add_devices(devices, ARRAY_SIZE(devices)); htcherald_disable_watchdog(); @@ -598,6 +593,8 @@ static void __init htcherald_init(void) htc_mmc_data[0] = &htc_mmc1_data; omap1_init_mmc(htc_mmc_data, 1); #endif + + omapfb_set_lcd_config(&htcherald_lcd_config); } MACHINE_START(HERALD, "HTC Herald") diff --git a/arch/arm/mach-omap1/board-innovator.c b/arch/arm/mach-omap1/board-innovator.c index 309369ea6978..1d5ab6606b9f 100644 --- a/arch/arm/mach-omap1/board-innovator.c +++ b/arch/arm/mach-omap1/board-innovator.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -370,10 +371,6 @@ static inline void innovator_mmc_init(void) } #endif -static struct omap_board_config_kernel innovator_config[] = { - { OMAP_TAG_LCD, NULL }, -}; - static void __init innovator_init(void) { if (cpu_is_omap1510()) @@ -416,17 +413,15 @@ static void __init innovator_init(void) #ifdef CONFIG_ARCH_OMAP15XX if (cpu_is_omap1510()) { omap1_usb_init(&innovator1510_usb_config); - innovator_config[1].data = &innovator1510_lcd_config; + omapfb_set_lcd_config(&innovator1510_lcd_config); } #endif #ifdef CONFIG_ARCH_OMAP16XX if (cpu_is_omap1610()) { omap1_usb_init(&h2_usb_config); - innovator_config[1].data = &innovator1610_lcd_config; + omapfb_set_lcd_config(&innovator1610_lcd_config); } #endif - omap_board_config = innovator_config; - omap_board_config_size = ARRAY_SIZE(innovator_config); omap_serial_init(); omap_register_i2c_bus(1, 100, NULL, 0); innovator_mmc_init(); diff --git a/arch/arm/mach-omap1/board-nokia770.c b/arch/arm/mach-omap1/board-nokia770.c index 7155d8ce516a..9b6332a31fb6 100644 --- a/arch/arm/mach-omap1/board-nokia770.c +++ b/arch/arm/mach-omap1/board-nokia770.c @@ -98,15 +98,16 @@ static struct mipid_platform_data nokia770_mipid_platform_data = { .shutdown = mipid_shutdown, }; +static struct omap_lcd_config nokia770_lcd_config __initdata = { + .ctrl_name = "hwa742", +}; + static void __init mipid_dev_init(void) { - const struct omap_lcd_config *conf; + nokia770_mipid_platform_data.nreset_gpio = 13; + nokia770_mipid_platform_data.data_lines = 16; - conf = omap_get_config(OMAP_TAG_LCD, struct omap_lcd_config); - if (conf != NULL) { - nokia770_mipid_platform_data.nreset_gpio = conf->nreset_gpio; - nokia770_mipid_platform_data.data_lines = conf->data_lines; - } + omapfb_set_lcd_config(&nokia770_lcd_config); } static void __init ads7846_dev_init(void) diff --git a/arch/arm/mach-omap1/board-osk.c b/arch/arm/mach-omap1/board-osk.c index 675de06557aa..ef874655fbd3 100644 --- a/arch/arm/mach-omap1/board-osk.c +++ b/arch/arm/mach-omap1/board-osk.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -300,12 +301,6 @@ static struct omap_lcd_config osk_lcd_config __initdata = { }; #endif -static struct omap_board_config_kernel osk_config[] __initdata = { -#ifdef CONFIG_OMAP_OSK_MISTRAL - { OMAP_TAG_LCD, &osk_lcd_config }, -#endif -}; - #ifdef CONFIG_OMAP_OSK_MISTRAL #include @@ -549,8 +544,6 @@ static void __init osk_init(void) osk_flash_resource.end = osk_flash_resource.start = omap_cs3_phys(); osk_flash_resource.end += SZ_32M - 1; platform_add_devices(osk5912_devices, ARRAY_SIZE(osk5912_devices)); - omap_board_config = osk_config; - omap_board_config_size = ARRAY_SIZE(osk_config); l = omap_readl(USB_TRANSCEIVER_CTRL); l |= (3 << 1); @@ -567,6 +560,11 @@ static void __init osk_init(void) omap_register_i2c_bus(1, 400, osk_i2c_board_info, ARRAY_SIZE(osk_i2c_board_info)); osk_mistral_init(); + +#ifdef CONFIG_OMAP_OSK_MISTRAL + omapfb_set_lcd_config(&osk_lcd_config); +#endif + } MACHINE_START(OMAP_OSK, "TI-OSK") diff --git a/arch/arm/mach-omap1/board-palmte.c b/arch/arm/mach-omap1/board-palmte.c index 81fa27f88369..612342cb2a2d 100644 --- a/arch/arm/mach-omap1/board-palmte.c +++ b/arch/arm/mach-omap1/board-palmte.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -209,10 +210,6 @@ static struct omap_lcd_config palmte_lcd_config __initdata = { .ctrl_name = "internal", }; -static struct omap_board_config_kernel palmte_config[] __initdata = { - { OMAP_TAG_LCD, &palmte_lcd_config }, -}; - static struct spi_board_info palmte_spi_info[] __initdata = { { .modalias = "tsc2102", @@ -250,9 +247,6 @@ static void __init omap_palmte_init(void) omap_cfg_reg(UART3_TX); omap_cfg_reg(UART3_RX); - omap_board_config = palmte_config; - omap_board_config_size = ARRAY_SIZE(palmte_config); - platform_add_devices(palmte_devices, ARRAY_SIZE(palmte_devices)); spi_register_board_info(palmte_spi_info, ARRAY_SIZE(palmte_spi_info)); @@ -260,6 +254,8 @@ static void __init omap_palmte_init(void) omap_serial_init(); omap1_usb_init(&palmte_usb_config); omap_register_i2c_bus(1, 100, NULL, 0); + + omapfb_set_lcd_config(&palmte_lcd_config); } MACHINE_START(OMAP_PALMTE, "OMAP310 based Palm Tungsten E") diff --git a/arch/arm/mach-omap1/board-palmtt.c b/arch/arm/mach-omap1/board-palmtt.c index 81cb82178388..b63350bc88fd 100644 --- a/arch/arm/mach-omap1/board-palmtt.c +++ b/arch/arm/mach-omap1/board-palmtt.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -273,10 +274,6 @@ static struct omap_lcd_config palmtt_lcd_config __initdata = { .ctrl_name = "internal", }; -static struct omap_board_config_kernel palmtt_config[] __initdata = { - { OMAP_TAG_LCD, &palmtt_lcd_config }, -}; - static void __init omap_mpu_wdt_mode(int mode) { if (mode) omap_writew(0x8000, OMAP_WDT_TIMER_MODE); @@ -298,15 +295,14 @@ static void __init omap_palmtt_init(void) omap_mpu_wdt_mode(0); - omap_board_config = palmtt_config; - omap_board_config_size = ARRAY_SIZE(palmtt_config); - platform_add_devices(palmtt_devices, ARRAY_SIZE(palmtt_devices)); spi_register_board_info(palmtt_boardinfo,ARRAY_SIZE(palmtt_boardinfo)); omap_serial_init(); omap1_usb_init(&palmtt_usb_config); omap_register_i2c_bus(1, 100, NULL, 0); + + omapfb_set_lcd_config(&palmtt_lcd_config); } MACHINE_START(OMAP_PALMTT, "OMAP1510 based Palm Tungsten|T") diff --git a/arch/arm/mach-omap1/board-palmz71.c b/arch/arm/mach-omap1/board-palmz71.c index e881945ce8ec..9924c70af09f 100644 --- a/arch/arm/mach-omap1/board-palmz71.c +++ b/arch/arm/mach-omap1/board-palmz71.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -239,10 +240,6 @@ static struct omap_lcd_config palmz71_lcd_config __initdata = { .ctrl_name = "internal", }; -static struct omap_board_config_kernel palmz71_config[] __initdata = { - {OMAP_TAG_LCD, &palmz71_lcd_config}, -}; - static irqreturn_t palmz71_powercable(int irq, void *dev_id) { @@ -313,9 +310,6 @@ omap_palmz71_init(void) palmz71_gpio_setup(1); omap_mpu_wdt_mode(0); - omap_board_config = palmz71_config; - omap_board_config_size = ARRAY_SIZE(palmz71_config); - platform_add_devices(devices, ARRAY_SIZE(devices)); spi_register_board_info(palmz71_boardinfo, @@ -324,6 +318,8 @@ omap_palmz71_init(void) omap_serial_init(); omap_register_i2c_bus(1, 100, NULL, 0); palmz71_gpio_setup(0); + + omapfb_set_lcd_config(&palmz71_lcd_config); } MACHINE_START(OMAP_PALMZ71, "OMAP310 based Palm Zire71") diff --git a/arch/arm/mach-omap1/board-perseus2.c b/arch/arm/mach-omap1/board-perseus2.c index c000bed76276..44df93172653 100644 --- a/arch/arm/mach-omap1/board-perseus2.c +++ b/arch/arm/mach-omap1/board-perseus2.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -249,10 +250,6 @@ static struct omap_lcd_config perseus2_lcd_config __initdata = { .ctrl_name = "internal", }; -static struct omap_board_config_kernel perseus2_config[] __initdata = { - { OMAP_TAG_LCD, &perseus2_lcd_config }, -}; - static void __init perseus2_init_smc91x(void) { fpga_write(1, H2P2_DBG_FPGA_LAN_RESET); @@ -320,10 +317,10 @@ static void __init omap_perseus2_init(void) platform_add_devices(devices, ARRAY_SIZE(devices)); - omap_board_config = perseus2_config; - omap_board_config_size = ARRAY_SIZE(perseus2_config); omap_serial_init(); omap_register_i2c_bus(1, 100, NULL, 0); + + omapfb_set_lcd_config(&perseus2_lcd_config); } /* Only FPGA needs to be mapped here. All others are done with ioremap */ diff --git a/arch/arm/mach-omap1/board-sx1.c b/arch/arm/mach-omap1/board-sx1.c index 7bcd82ab0fd0..0c880be11276 100644 --- a/arch/arm/mach-omap1/board-sx1.c +++ b/arch/arm/mach-omap1/board-sx1.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -371,11 +372,6 @@ static struct platform_device *sx1_devices[] __initdata = { &sx1_lcd_device, &sx1_irda_device, }; -/*-----------------------------------------*/ - -static struct omap_board_config_kernel sx1_config[] __initdata = { - { OMAP_TAG_LCD, &sx1_lcd_config }, -}; /*-----------------------------------------*/ @@ -391,8 +387,6 @@ static void __init omap_sx1_init(void) platform_add_devices(sx1_devices, ARRAY_SIZE(sx1_devices)); - omap_board_config = sx1_config; - omap_board_config_size = ARRAY_SIZE(sx1_config); omap_serial_init(); omap_register_i2c_bus(1, 100, NULL, 0); omap1_usb_init(&sx1_usb_config); @@ -406,6 +400,8 @@ static void __init omap_sx1_init(void) gpio_direction_output(1, 1); /*A_IRDA_OFF = 1 */ gpio_direction_output(11, 0); /*A_SWITCH = 0 */ gpio_direction_output(15, 0); /*A_USB_ON = 0 */ + + omapfb_set_lcd_config(&sx1_lcd_config); } MACHINE_START(SX1, "OMAP310 based Siemens SX1") diff --git a/arch/arm/plat-omap/fb.c b/arch/arm/plat-omap/fb.c index 45f35c983f44..24e62693b809 100644 --- a/arch/arm/plat-omap/fb.c +++ b/arch/arm/plat-omap/fb.c @@ -37,6 +37,7 @@ #if defined(CONFIG_FB_OMAP) || defined(CONFIG_FB_OMAP_MODULE) +static bool omapfb_lcd_configured; static struct omapfb_platform_data omapfb_config; static u64 omap_fb_dma_mask = ~(u32)0; @@ -52,16 +53,21 @@ static struct platform_device omap_fb_device = { .num_resources = 0, }; -static int __init omap_init_fb(void) +void __init omapfb_set_lcd_config(const struct omap_lcd_config *config) { - const struct omap_lcd_config *conf; + omapfb_config.lcd = *config; + omapfb_lcd_configured = true; +} - conf = omap_get_config(OMAP_TAG_LCD, struct omap_lcd_config); - if (conf == NULL) +static int __init omap_init_fb(void) +{ + /* + * If the board file has not set the lcd config with + * omapfb_set_lcd_config(), don't bother registering the omapfb device + */ + if (!omapfb_lcd_configured) return 0; - omapfb_config.lcd = *conf; - return platform_device_register(&omap_fb_device); } @@ -90,4 +96,8 @@ static int __init omap_init_fb(void) arch_initcall(omap_init_fb); +#else + +void __init omapfb_set_lcd_config(omap_lcd_config *config) { } + #endif diff --git a/include/linux/omapfb.h b/include/linux/omapfb.h index a575706a83c3..4ff57e81051d 100644 --- a/include/linux/omapfb.h +++ b/include/linux/omapfb.h @@ -226,6 +226,8 @@ struct omapfb_platform_data { struct omap_lcd_config lcd; }; +void __init omapfb_set_lcd_config(const struct omap_lcd_config *config); + #endif #endif /* __OMAPFB_H */ -- cgit v1.2.3 From 9ad52e63db1bc588636bc66b9133498c46e6535c Mon Sep 17 00:00:00 2001 From: Anthony PERARD Date: Tue, 21 Feb 2012 16:22:10 +0000 Subject: PCI: Add PCI_EXP_TYPE_PCIE_BRIDGE value Signed-off-by: Anthony PERARD Signed-off-by: Jesse Barnes --- include/linux/pci_regs.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include/linux') diff --git a/include/linux/pci_regs.h b/include/linux/pci_regs.h index e41a10f5ae83..4b608f543412 100644 --- a/include/linux/pci_regs.h +++ b/include/linux/pci_regs.h @@ -391,6 +391,7 @@ #define PCI_EXP_TYPE_UPSTREAM 0x5 /* Upstream Port */ #define PCI_EXP_TYPE_DOWNSTREAM 0x6 /* Downstream Port */ #define PCI_EXP_TYPE_PCI_BRIDGE 0x7 /* PCI/PCI-X Bridge */ +#define PCI_EXP_TYPE_PCIE_BRIDGE 0x8 /* PCI/PCI-X to PCIE Bridge */ #define PCI_EXP_TYPE_RC_END 0x9 /* Root Complex Integrated Endpoint */ #define PCI_EXP_TYPE_RC_EC 0xa /* Root Complex Event Collector */ #define PCI_EXP_FLAGS_SLOT 0x0100 /* Slot implemented */ -- cgit v1.2.3 From 34a4876e3071ddebf3c98c99ba01c14b059a1361 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Sat, 11 Feb 2012 00:18:41 -0800 Subject: PCI: move pci_find_saved_cap out of linux/pci.h Only one user in driver/pci/pci.c, so we don't need to put it in global pci.h Reviewed-by: Bjorn Helgaas Signed-off-by: Yinghai Lu Signed-off-by: Jesse Barnes --- drivers/pci/pci.c | 19 +++++++++++++++++++ include/linux/pci.h | 19 ------------------- 2 files changed, 19 insertions(+), 19 deletions(-) (limited to 'include/linux') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index e7dfcd447571..8f30736dbf3f 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -825,6 +825,19 @@ EXPORT_SYMBOL(pci_choose_state); #define pcie_cap_has_sltctl2(type, flags) \ ((flags & PCI_EXP_FLAGS_VERS) > 1) +static struct pci_cap_saved_state *pci_find_saved_cap( + struct pci_dev *pci_dev, char cap) +{ + struct pci_cap_saved_state *tmp; + struct hlist_node *pos; + + hlist_for_each_entry(tmp, pos, &pci_dev->saved_cap_space, next) { + if (tmp->cap.cap_nr == cap) + return tmp; + } + return NULL; +} + static int pci_save_pcie_state(struct pci_dev *dev) { int pos, i = 0; @@ -1869,6 +1882,12 @@ void platform_pci_wakeup_init(struct pci_dev *dev) platform_pci_sleep_wake(dev, false); } +static void pci_add_saved_cap(struct pci_dev *pci_dev, + struct pci_cap_saved_state *new_cap) +{ + hlist_add_head(&new_cap->next, &pci_dev->saved_cap_space); +} + /** * pci_add_save_buffer - allocate buffer for saving given capability registers * @dev: the PCI device diff --git a/include/linux/pci.h b/include/linux/pci.h index f8caaab4b3fc..bcaa51ca7858 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -368,25 +368,6 @@ static inline int pci_channel_offline(struct pci_dev *pdev) return (pdev->error_state != pci_channel_io_normal); } -static inline struct pci_cap_saved_state *pci_find_saved_cap( - struct pci_dev *pci_dev, char cap) -{ - struct pci_cap_saved_state *tmp; - struct hlist_node *pos; - - hlist_for_each_entry(tmp, pos, &pci_dev->saved_cap_space, next) { - if (tmp->cap.cap_nr == cap) - return tmp; - } - return NULL; -} - -static inline void pci_add_saved_cap(struct pci_dev *pci_dev, - struct pci_cap_saved_state *new_cap) -{ - hlist_add_head(&new_cap->next, &pci_dev->saved_cap_space); -} - /* * The first PCI_BRIDGE_RESOURCE_NUM PCI bus resources (those that correspond * to P2P or CardBus bridge windows) go in a table. Additional ones (for -- cgit v1.2.3 From 5a21d70dbd33d20713fb735ad9381711b0ae2c9b Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Thu, 23 Feb 2012 20:18:59 -0700 Subject: PCI: add struct pci_host_bridge and a list of all bridges found This adds a list of all PCI host bridges we find and a way to look up the host bridge from a pci_dev. Signed-off-by: Bjorn Helgaas --- drivers/pci/probe.c | 39 ++++++++++++++++++++++++++++++++++----- include/linux/pci.h | 5 +++++ 2 files changed, 39 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index e4c0d1c6324d..3a30023a123c 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -15,6 +15,8 @@ #define CARDBUS_LATENCY_TIMER 176 /* secondary latency timer */ #define CARDBUS_RESERVE_BUSNR 3 +static LIST_HEAD(pci_host_bridges); + /* Ugh. Need to stop exporting this to modules. */ LIST_HEAD(pci_root_buses); EXPORT_SYMBOL(pci_root_buses); @@ -42,6 +44,23 @@ int no_pci_devices(void) } EXPORT_SYMBOL(no_pci_devices); +static struct pci_host_bridge *pci_host_bridge(struct pci_dev *dev) +{ + struct pci_bus *bus; + struct pci_host_bridge *bridge; + + bus = dev->bus; + while (bus->parent) + bus = bus->parent; + + list_for_each_entry(bridge, &pci_host_bridges, list) { + if (bridge->bus == bus) + return bridge; + } + + return NULL; +} + /* * PCI Bus Class */ @@ -1544,20 +1563,23 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus, struct pci_ops *ops, void *sysdata, struct list_head *resources) { int error, i; + struct pci_host_bridge *bridge; struct pci_bus *b, *b2; struct device *dev; struct pci_bus_resource *bus_res, *n; struct resource *res; + bridge = kzalloc(sizeof(*bridge), GFP_KERNEL); + if (!bridge) + return NULL; + b = pci_alloc_bus(); if (!b) - return NULL; + goto err_bus; dev = kzalloc(sizeof(*dev), GFP_KERNEL); - if (!dev) { - kfree(b); - return NULL; - } + if (!dev) + goto err_dev; b->sysdata = sysdata; b->ops = ops; @@ -1594,6 +1616,8 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus, b->number = b->secondary = bus; + bridge->bus = b; + /* Add initial resources to the bus */ list_for_each_entry_safe(bus_res, n, resources, list) list_move_tail(&bus_res->list, &b->resources); @@ -1609,6 +1633,7 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus, } down_write(&pci_bus_sem); + list_add_tail(&bridge->list, &pci_host_bridges); list_add_tail(&b->node, &pci_root_buses); up_write(&pci_bus_sem); @@ -1618,11 +1643,15 @@ class_dev_reg_err: device_unregister(dev); dev_reg_err: down_write(&pci_bus_sem); + list_del(&bridge->list); list_del(&b->node); up_write(&pci_bus_sem); err_out: kfree(dev); +err_dev: kfree(b); +err_bus: + kfree(bridge); return NULL; } diff --git a/include/linux/pci.h b/include/linux/pci.h index bcaa51ca7858..2c946b3bbf77 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -368,6 +368,11 @@ static inline int pci_channel_offline(struct pci_dev *pdev) return (pdev->error_state != pci_channel_io_normal); } +struct pci_host_bridge { + struct list_head list; + struct pci_bus *bus; /* root bus */ +}; + /* * The first PCI_BRIDGE_RESOURCE_NUM PCI bus resources (those that correspond * to P2P or CardBus bridge windows) go in a table. Additional ones (for -- cgit v1.2.3 From 0efd5aab41e18a1175f72641696cfda154ba6c87 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Thu, 23 Feb 2012 20:19:00 -0700 Subject: PCI: add struct pci_host_bridge_window with CPU/bus address offset Some PCI host bridges apply an address offset, so bus addresses on PCI are different from CPU addresses. This patch adds a way for architectures to tell the PCI core about this offset. For example: LIST_HEAD(resources); pci_add_resource_offset(&resources, host->io_space, host->io_offset); pci_add_resource_offset(&resources, host->mem_space, host->mem_offset); pci_scan_root_bus(parent, bus, ops, sysdata, &resources); Signed-off-by: Bjorn Helgaas --- drivers/pci/bus.c | 30 +++++++++++++++++++----------- drivers/pci/probe.c | 32 +++++++++++++++++++++++--------- include/linux/pci.h | 9 +++++++++ 3 files changed, 51 insertions(+), 20 deletions(-) (limited to 'include/linux') diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c index 398f5d859791..4ce5ef2f2826 100644 --- a/drivers/pci/bus.c +++ b/drivers/pci/bus.c @@ -18,28 +18,36 @@ #include "pci.h" -void pci_add_resource(struct list_head *resources, struct resource *res) +void pci_add_resource_offset(struct list_head *resources, struct resource *res, + resource_size_t offset) { - struct pci_bus_resource *bus_res; + struct pci_host_bridge_window *window; - bus_res = kzalloc(sizeof(struct pci_bus_resource), GFP_KERNEL); - if (!bus_res) { - printk(KERN_ERR "PCI: can't add bus resource %pR\n", res); + window = kzalloc(sizeof(struct pci_host_bridge_window), GFP_KERNEL); + if (!window) { + printk(KERN_ERR "PCI: can't add host bridge window %pR\n", res); return; } - bus_res->res = res; - list_add_tail(&bus_res->list, resources); + window->res = res; + window->offset = offset; + list_add_tail(&window->list, resources); +} +EXPORT_SYMBOL(pci_add_resource_offset); + +void pci_add_resource(struct list_head *resources, struct resource *res) +{ + pci_add_resource_offset(resources, res, 0); } EXPORT_SYMBOL(pci_add_resource); void pci_free_resource_list(struct list_head *resources) { - struct pci_bus_resource *bus_res, *tmp; + struct pci_host_bridge_window *window, *tmp; - list_for_each_entry_safe(bus_res, tmp, resources, list) { - list_del(&bus_res->list); - kfree(bus_res); + list_for_each_entry_safe(window, tmp, resources, list) { + list_del(&window->list); + kfree(window); } } EXPORT_SYMBOL(pci_free_resource_list); diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 3a30023a123c..3f07cb6bae32 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1562,12 +1562,15 @@ unsigned int __devinit pci_scan_child_bus(struct pci_bus *bus) struct pci_bus *pci_create_root_bus(struct device *parent, int bus, struct pci_ops *ops, void *sysdata, struct list_head *resources) { - int error, i; + int error; struct pci_host_bridge *bridge; struct pci_bus *b, *b2; struct device *dev; - struct pci_bus_resource *bus_res, *n; + struct pci_host_bridge_window *window, *n; struct resource *res; + resource_size_t offset; + char bus_addr[64]; + char *fmt; bridge = kzalloc(sizeof(*bridge), GFP_KERNEL); if (!bridge) @@ -1617,19 +1620,30 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus, b->number = b->secondary = bus; bridge->bus = b; - - /* Add initial resources to the bus */ - list_for_each_entry_safe(bus_res, n, resources, list) - list_move_tail(&bus_res->list, &b->resources); + INIT_LIST_HEAD(&bridge->windows); if (parent) dev_info(parent, "PCI host bridge to bus %s\n", dev_name(&b->dev)); else printk(KERN_INFO "PCI host bridge to bus %s\n", dev_name(&b->dev)); - pci_bus_for_each_resource(b, res, i) { - if (res) - dev_info(&b->dev, "root bus resource %pR\n", res); + /* Add initial resources to the bus */ + list_for_each_entry_safe(window, n, resources, list) { + list_move_tail(&window->list, &bridge->windows); + res = window->res; + offset = window->offset; + pci_bus_add_resource(b, res, 0); + if (offset) { + if (resource_type(res) == IORESOURCE_IO) + fmt = " (bus address [%#06llx-%#06llx])"; + else + fmt = " (bus address [%#010llx-%#010llx])"; + snprintf(bus_addr, sizeof(bus_addr), fmt, + (unsigned long long) (res->start - offset), + (unsigned long long) (res->end - offset)); + } else + bus_addr[0] = '\0'; + dev_info(&b->dev, "root bus resource %pR%s\n", res, bus_addr); } down_write(&pci_bus_sem); diff --git a/include/linux/pci.h b/include/linux/pci.h index 2c946b3bbf77..419f78f96375 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -368,9 +368,16 @@ static inline int pci_channel_offline(struct pci_dev *pdev) return (pdev->error_state != pci_channel_io_normal); } +struct pci_host_bridge_window { + struct list_head list; + struct resource *res; /* host bridge aperture (CPU address) */ + resource_size_t offset; /* bus address + offset = CPU address */ +}; + struct pci_host_bridge { struct list_head list; struct pci_bus *bus; /* root bus */ + struct list_head windows; /* pci_host_bridge_windows */ }; /* @@ -901,6 +908,8 @@ void pci_release_selected_regions(struct pci_dev *, int); /* drivers/pci/bus.c */ void pci_add_resource(struct list_head *resources, struct resource *res); +void pci_add_resource_offset(struct list_head *resources, struct resource *res, + resource_size_t offset); void pci_free_resource_list(struct list_head *resources); void pci_bus_add_resource(struct pci_bus *bus, struct resource *res, unsigned int flags); struct resource *pci_bus_resource_n(const struct pci_bus *bus, int n); -- cgit v1.2.3 From 36a66cd6fd0a70ac6848d740d9cf7a4360b5776a Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Thu, 23 Feb 2012 20:19:00 -0700 Subject: PCI: add generic pcibios_resource_to_bus() This replaces the generic versions of pcibios_resource_to_bus() and pcibios_bus_to_resource() in asm-generic/pci.h with versions that use pci_resource_to_bus() and pci_bus_to_resource(). The replacements are equivalent except that they can apply host bridge window offsets when the arch has supplied them by using pci_add_resource_offset(). Each arch can convert to using pci_add_resource_offset() individually by removing its device resource fixups from pcibios_fixup_bus() and supplying ARCH_HAS_GENERIC_PCI_OFFSETS. ARCH_HAS_GENERIC_PCI_OFFSETS can be removed after all have converted. Signed-off-by: Bjorn Helgaas --- drivers/pci/probe.c | 16 ++++++++++++++++ include/asm-generic/pci.h | 24 +----------------------- include/linux/pci.h | 4 ++++ 3 files changed, 21 insertions(+), 23 deletions(-) (limited to 'include/linux') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 3539171d8a98..a677b1e995de 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -118,6 +118,22 @@ void pci_bus_to_resource(struct pci_dev *dev, struct resource *res, res->end = region->end + offset; } +#ifdef ARCH_HAS_GENERIC_PCI_OFFSETS +void pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region, + struct resource *res) +{ + pci_resource_to_bus(dev, region, res); +} +EXPORT_SYMBOL(pcibios_resource_to_bus); + +void pcibios_bus_to_resource(struct pci_dev *dev, struct resource *res, + struct pci_bus_region *region) +{ + pci_bus_to_resource(dev, res, region); +} +EXPORT_SYMBOL(pcibios_bus_to_resource); +#endif + /* * PCI Bus Class */ diff --git a/include/asm-generic/pci.h b/include/asm-generic/pci.h index 26373cff4546..0410346e2cf6 100644 --- a/include/asm-generic/pci.h +++ b/include/asm-generic/pci.h @@ -6,29 +6,7 @@ #ifndef _ASM_GENERIC_PCI_H #define _ASM_GENERIC_PCI_H -/** - * pcibios_resource_to_bus - convert resource to PCI bus address - * @dev: device which owns this resource - * @region: converted bus-centric region (start,end) - * @res: resource to convert - * - * Convert a resource to a PCI device bus address or bus window. - */ -static inline void -pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region, - struct resource *res) -{ - region->start = res->start; - region->end = res->end; -} - -static inline void -pcibios_bus_to_resource(struct pci_dev *dev, struct resource *res, - struct pci_bus_region *region) -{ - res->start = region->start; - res->end = region->end; -} +#define ARCH_HAS_GENERIC_PCI_OFFSETS static inline struct resource * pcibios_select_root(struct pci_dev *pdev, struct resource *res) diff --git a/include/linux/pci.h b/include/linux/pci.h index 419f78f96375..be58a51706cc 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -648,6 +648,10 @@ void pci_fixup_cardbus(struct pci_bus *); /* Generic PCI functions used internally */ +void pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region, + struct resource *res); +void pcibios_bus_to_resource(struct pci_dev *dev, struct resource *res, + struct pci_bus_region *region); void pcibios_scan_specific_bus(int busn); extern struct pci_bus *pci_find_bus(int domain, int busnr); void pci_bus_add_devices(const struct pci_bus *bus); -- cgit v1.2.3 From 1681f5ae4ca25bddb6f7b6d4f463cc83e3d1ad01 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Wed, 22 Feb 2012 14:25:58 -0700 Subject: pinctrl: disallow map table entries with NULL dev_name field Hog entries are mapping table entries with .ctrl_dev_name == .dev_name. All other mapping table entries need .dev_name set so that they will match some pinctrl_get() call. All extant PIN_MAP*() macros set .dev_name. So, there is no reason to allow mapping table entries without .dev_name set. Update the code and documentation to disallow this. Signed-off-by: Stephen Warren Acked-by: Dong Aisheng Signed-off-by: Linus Walleij --- Documentation/pinctrl.txt | 15 +++------ drivers/pinctrl/core.c | 73 +++++++++++++---------------------------- include/linux/pinctrl/machine.h | 7 ---- 3 files changed, 27 insertions(+), 68 deletions(-) (limited to 'include/linux') diff --git a/Documentation/pinctrl.txt b/Documentation/pinctrl.txt index ee3266b948e7..fa9163af34f6 100644 --- a/Documentation/pinctrl.txt +++ b/Documentation/pinctrl.txt @@ -494,14 +494,10 @@ Definitions: {"map-i2c0", i2c0, pinctrl0, fi2c0, gi2c0} } - Every map must be assigned a symbolic name, pin controller and function. - The group is not compulsory - if it is omitted the first group presented by - the driver as applicable for the function will be selected, which is - useful for simple cases. - - The device name is present in map entries tied to specific devices. Maps - without device names are referred to as SYSTEM pinmuxes, such as can be taken - by the machine implementation on boot and not tied to any specific device. + Every map must be assigned a state name, pin controller, device and + function. The group is not compulsory - if it is omitted the first group + presented by the driver as applicable for the function will be selected, + which is useful for simple cases. It is possible to map several groups to the same combination of device, pin controller and function. This is for cases where a certain function on @@ -983,8 +979,7 @@ after this you should be able to see this in the debugfs listing of all pins. System pin control hogging ========================== -A system pin control map entry, i.e. a pin control setting that does not have -a device associated with it, can be hogged by the core when the pin controller +Pin control map entries can be hogged by the core when the pin controller is registered. This means that the core will attempt to call pinctrl_get() and pinctrl_enable() on it immediately after the pin control device has been registered. diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c index fb3fbb76932e..5411e32bb3f6 100644 --- a/drivers/pinctrl/core.c +++ b/drivers/pinctrl/core.c @@ -479,24 +479,21 @@ EXPORT_SYMBOL_GPL(pinctrl_gpio_direction_output); static struct pinctrl *pinctrl_get_locked(struct device *dev, const char *name) { struct pinctrl_dev *pctldev = NULL; - const char *devname = NULL; + const char *devname; struct pinctrl *p; - bool found_map; unsigned num_maps = 0; int ret = -ENODEV; struct pinctrl_maps *maps_node; int i; struct pinctrl_map const *map; - /* We must have dev or ID or both */ - if (!dev && !name) + /* We must have a dev name */ + if (WARN_ON(!dev)) return ERR_PTR(-EINVAL); - if (dev) - devname = dev_name(dev); + devname = dev_name(dev); - pr_debug("get pin control handle %s for device %s\n", name, - devname ? devname : "(none)"); + pr_debug("get pin control handle device %s state %s\n", devname, name); /* * create the state cookie holder struct pinctrl for each @@ -511,8 +508,6 @@ static struct pinctrl *pinctrl_get_locked(struct device *dev, const char *name) /* Iterate over the pin control maps to locate the right ones */ for_each_maps(maps_node, i, map) { - found_map = false; - /* * First, try to find the pctldev given in the map */ @@ -529,6 +524,10 @@ static struct pinctrl *pinctrl_get_locked(struct device *dev, const char *name) pr_debug("in map, found pctldev %s to handle function %s", dev_name(pctldev->dev), map->function); + /* Map must be for this device */ + if (strcmp(map->dev_name, devname)) + continue; + /* * If we're looking for a specific named map, this must match, * else we loop and look for the next. @@ -540,30 +539,12 @@ static struct pinctrl *pinctrl_get_locked(struct device *dev, const char *name) continue; } - /* - * This is for the case where no device name is given, we - * already know that the function name matches from above - * code. - */ - if (!map->dev_name && (name != NULL)) - found_map = true; - - /* If the mapping has a device set up it must match */ - if (map->dev_name && - (!devname || !strcmp(map->dev_name, devname))) - /* MATCH! */ - found_map = true; - - /* If this map is applicable, then apply it */ - if (found_map) { - ret = pinmux_apply_muxmap(pctldev, p, dev, - devname, map); - if (ret) { - kfree(p); - return ERR_PTR(ret); - } - num_maps++; + ret = pinmux_apply_muxmap(pctldev, p, dev, devname, map); + if (ret) { + kfree(p); + return ERR_PTR(ret); } + num_maps++; } /* @@ -578,9 +559,7 @@ static struct pinctrl *pinctrl_get_locked(struct device *dev, const char *name) dev_info(dev, "zero maps found for mapping %s\n", name); pr_debug("found %u mux maps for device %s, UD %s\n", - num_maps, - devname ? devname : "(anonymous)", - name ? name : "(undefined)"); + num_maps, devname, name ? name : "(undefined)"); /* Add the pinmux to the global list */ mutex_lock(&pinctrl_list_mutex); @@ -707,14 +686,11 @@ int pinctrl_register_mappings(struct pinctrl_map const *maps, return -EINVAL; } - if (!maps[i].dev_name) - pr_debug("add system map %s function %s with no device\n", - maps[i].name, - maps[i].function); - else - pr_debug("register map %s, function %s\n", - maps[i].name, - maps[i].function); + if (!maps[i].dev_name) { + pr_err("failed to register map %s (%d): no device given\n", + maps[i].name, i); + return -EINVAL; + } } maps_node = kzalloc(sizeof(*maps_node), GFP_KERNEL); @@ -938,13 +914,8 @@ static int pinctrl_maps_show(struct seq_file *s, void *what) mutex_lock(&pinctrl_maps_mutex); for_each_maps(maps_node, i, map) { seq_printf(s, "%s:\n", map->name); - if (map->dev_name) - seq_printf(s, " device: %s\n", - map->dev_name); - else - seq_printf(s, " SYSTEM MUX\n"); - seq_printf(s, " controlling device %s\n", - map->ctrl_dev_name); + seq_printf(s, " device: %s\n", map->dev_name); + seq_printf(s, " controlling device %s\n", map->ctrl_dev_name); seq_printf(s, " function: %s\n", map->function); seq_printf(s, " group: %s\n", map->group ? map->group : "(default)"); diff --git a/include/linux/pinctrl/machine.h b/include/linux/pinctrl/machine.h index af145d571970..400f1926b234 100644 --- a/include/linux/pinctrl/machine.h +++ b/include/linux/pinctrl/machine.h @@ -45,13 +45,6 @@ struct pinctrl_map { #define PIN_MAP(a, b, c, d) \ { .name = a, .ctrl_dev_name = b, .function = c, .dev_name = d } -/* - * Convenience macro to map a system function onto a certain pinctrl device. - * System functions are not assigned to a particular device. - */ -#define PIN_MAP_SYS(a, b, c) \ - { .name = a, .ctrl_dev_name = b, .function = c } - /* * Convenience macro to map a system function onto a certain pinctrl device, * to be hogged by the pin control core until the system shuts down. -- cgit v1.2.3 From 4d4cfd1656b5f6c88eae51c40741a695b108b006 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 23 Feb 2012 20:53:37 +0000 Subject: regmap: Allow drivers to sync only part of the register cache Provide a regcache_sync_region() operation which allows drivers to write only part of the cache back to the hardware. This is intended for use in cases like power domains or DSP memories where part of the device register map may be reset without fully resetting the device. Fully supporting these devices is likely to require additional work to make specific regions of the register map cache only while they are in reset, but this is enough for most devices. Signed-off-by: Mark Brown --- drivers/base/regmap/regcache.c | 45 ++++++++++++++++++++++++++++++++++++++++++ include/linux/regmap.h | 2 ++ 2 files changed, 47 insertions(+) (limited to 'include/linux') diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index aec5a7486a29..b35f8751471d 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -298,6 +298,51 @@ out: } EXPORT_SYMBOL_GPL(regcache_sync); +/** + * regcache_sync_region: Sync part of the register cache with the hardware. + * + * @map: map to sync. + * @min: first register to sync + * @max: last register to sync + * + * Write all non-default register values in the specified region to + * the hardware. + * + * Return a negative value on failure, 0 on success. + */ +int regcache_sync_region(struct regmap *map, unsigned int min, + unsigned int max) +{ + int ret = 0; + const char *name; + unsigned int bypass; + + BUG_ON(!map->cache_ops || !map->cache_ops->sync); + + mutex_lock(&map->lock); + + /* Remember the initial bypass state */ + bypass = map->cache_bypass; + + name = map->cache_ops->name; + dev_dbg(map->dev, "Syncing %s cache from %d-%d\n", name, min, max); + + trace_regcache_sync(map->dev, name, "start region"); + + if (!map->cache_dirty) + goto out; + + ret = map->cache_ops->sync(map, min, max); + +out: + trace_regcache_sync(map->dev, name, "stop region"); + /* Restore the bypass state */ + map->cache_bypass = bypass; + mutex_unlock(&map->lock); + + return ret; +} + /** * regcache_cache_only: Put a register map into cache only mode * diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 860739a8a6dd..5ff7d44730e5 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -145,6 +145,8 @@ int regmap_update_bits_check(struct regmap *map, unsigned int reg, bool *change); int regcache_sync(struct regmap *map); +int regcache_sync_region(struct regmap *map, unsigned int min, + unsigned int max); void regcache_cache_only(struct regmap *map, bool enable); void regcache_cache_bypass(struct regmap *map, bool enable); void regcache_mark_dirty(struct regmap *map); -- cgit v1.2.3 From 64eac23196e9cb7cad63f3c747928cc53d2699b5 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Fri, 24 Feb 2012 07:41:06 +0100 Subject: misc: at24: describe platform_data with kernel_doc There have been questions about its fields lately, so describe them properly. Reported-by: Yegor Yefremov Signed-off-by: Wolfram Sang --- include/linux/i2c/at24.h | 35 +++++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/include/linux/i2c/at24.h b/include/linux/i2c/at24.h index 8ace93024d60..285025a9cdc9 100644 --- a/include/linux/i2c/at24.h +++ b/include/linux/i2c/at24.h @@ -1,19 +1,42 @@ +/* + * at24.h - platform_data for the at24 (generic eeprom) driver + * (C) Copyright 2008 by Pengutronix + * (C) Copyright 2012 by Wolfram Sang + * same license as the driver + */ + #ifndef _LINUX_AT24_H #define _LINUX_AT24_H #include #include -/* - * As seen through Linux I2C, differences between the most common types of I2C - * memory include: - * - How much memory is available (usually specified in bit)? - * - What write page size does it support? - * - Special flags (16 bit addresses, read_only, world readable...)? +/** + * struct at24_platform_data - data to set up at24 (generic eeprom) driver + * @byte_len: size of eeprom in byte + * @page_size: number of byte which can be written in one go + * @flags: tunable options, check AT24_FLAG_* defines + * @setup: an optional callback invoked after eeprom is probed; enables kernel + code to access eeprom via memory_accessor, see example + * @context: optional parameter passed to setup() * * If you set up a custom eeprom type, please double-check the parameters. * Especially page_size needs extra care, as you risk data loss if your value * is bigger than what the chip actually supports! + * + * An example in pseudo code for a setup() callback: + * + * void get_mac_addr(struct memory_accessor *mem_acc, void *context) + * { + * u8 *mac_addr = ethernet_pdata->mac_addr; + * off_t offset = context; + * + * // Read MAC addr from EEPROM + * if (mem_acc->read(mem_acc, mac_addr, offset, ETH_ALEN) == ETH_ALEN) + * pr_info("Read MAC addr from EEPROM: %pM\n", mac_addr); + * } + * + * This function pointer and context can now be set up in at24_platform_data. */ struct at24_platform_data { -- cgit v1.2.3 From f4ca5c6a56278ca5421bc2e40422e4155b6735d8 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Thu, 23 Feb 2012 23:46:49 -0800 Subject: PCI: Add class support in quirk handling Recently added support to allow quirks to report duration also make the boot log very crowded when initcall_debug is specified. One thing we can to do mitigate this is to not call quirks unnecessarily by adding a new quirk declaration macro that takes a class argument. The new macro takes a class value and a class shift value (since it can vary) so that quirks will be limited to certain device classes, greatly reducing the number we call on every PCI device addition. -v2: fix v1 that left over of sparated patch. -v3: according to Jesse, change cls to class, cls_shift, to class_shift. Signed-off-by: Yinghai Lu Signed-off-by: Jesse Barnes --- drivers/pci/quirks.c | 12 +++++----- include/linux/pci.h | 63 +++++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 59 insertions(+), 16 deletions(-) (limited to 'include/linux') diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index fb544d6d29f6..2b4b1ea158cf 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -2961,17 +2961,19 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x010a, disable_igfx_irq); static void pci_do_fixups(struct pci_dev *dev, struct pci_fixup *f, struct pci_fixup *end) { - while (f < end) { - if ((f->vendor == dev->vendor || f->vendor == (u16) PCI_ANY_ID) && - (f->device == dev->device || f->device == (u16) PCI_ANY_ID)) { + for (; f < end; f++) + if ((f->class == (u32) (dev->class >> f->class_shift) || + f->class == (u32) PCI_ANY_ID) && + (f->vendor == dev->vendor || + f->vendor == (u16) PCI_ANY_ID) && + (f->device == dev->device || + f->device == (u16) PCI_ANY_ID)) { dev_dbg(&dev->dev, "calling %pF\n", f->hook); if (initcall_debug) do_one_fixup_debug(f->hook, dev); else f->hook(dev); } - f++; - } } extern struct pci_fixup __start_pci_fixups_early[]; diff --git a/include/linux/pci.h b/include/linux/pci.h index be58a51706cc..74a20bdc93f6 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1382,7 +1382,10 @@ static inline void pci_resource_to_user(const struct pci_dev *dev, int bar, */ struct pci_fixup { - u16 vendor, device; /* You can use PCI_ANY_ID here of course */ + u16 vendor; /* You can use PCI_ANY_ID here of course */ + u16 device; /* You can use PCI_ANY_ID here of course */ + u32 class; /* You can use PCI_ANY_ID here too */ + unsigned int class_shift; /* should be 0, 8, 16 */ void (*hook)(struct pci_dev *dev); }; @@ -1397,30 +1400,68 @@ enum pci_fixup_pass { }; /* Anonymous variables would be nice... */ -#define DECLARE_PCI_FIXUP_SECTION(section, name, vendor, device, hook) \ - static const struct pci_fixup __pci_fixup_##name __used \ - __attribute__((__section__(#section))) = { vendor, device, hook }; +#define DECLARE_PCI_FIXUP_SECTION(section, name, vendor, device, class, \ + class_shift, hook) \ + static const struct pci_fixup const __pci_fixup_##name __used \ + __attribute__((__section__(#section), aligned((sizeof(void *))))) \ + = { vendor, device, class, class_shift, hook }; + +#define DECLARE_PCI_FIXUP_CLASS_EARLY(vendor, device, class, \ + class_shift, hook) \ + DECLARE_PCI_FIXUP_SECTION(.pci_fixup_early, \ + vendor##device##hook, vendor, device, class, class_shift, hook) +#define DECLARE_PCI_FIXUP_CLASS_HEADER(vendor, device, class, \ + class_shift, hook) \ + DECLARE_PCI_FIXUP_SECTION(.pci_fixup_header, \ + vendor##device##hook, vendor, device, class, class_shift, hook) +#define DECLARE_PCI_FIXUP_CLASS_FINAL(vendor, device, class, \ + class_shift, hook) \ + DECLARE_PCI_FIXUP_SECTION(.pci_fixup_final, \ + vendor##device##hook, vendor, device, class, class_shift, hook) +#define DECLARE_PCI_FIXUP_CLASS_ENABLE(vendor, device, class, \ + class_shift, hook) \ + DECLARE_PCI_FIXUP_SECTION(.pci_fixup_enable, \ + vendor##device##hook, vendor, device, class, class_shift, hook) +#define DECLARE_PCI_FIXUP_CLASS_RESUME(vendor, device, class, \ + class_shift, hook) \ + DECLARE_PCI_FIXUP_SECTION(.pci_fixup_resume, \ + resume##vendor##device##hook, vendor, device, class, \ + class_shift, hook) +#define DECLARE_PCI_FIXUP_CLASS_RESUME_EARLY(vendor, device, class, \ + class_shift, hook) \ + DECLARE_PCI_FIXUP_SECTION(.pci_fixup_resume_early, \ + resume_early##vendor##device##hook, vendor, device, \ + class, class_shift, hook) +#define DECLARE_PCI_FIXUP_CLASS_SUSPEND(vendor, device, class, \ + class_shift, hook) \ + DECLARE_PCI_FIXUP_SECTION(.pci_fixup_suspend, \ + suspend##vendor##device##hook, vendor, device, class, \ + class_shift, hook) + #define DECLARE_PCI_FIXUP_EARLY(vendor, device, hook) \ DECLARE_PCI_FIXUP_SECTION(.pci_fixup_early, \ - vendor##device##hook, vendor, device, hook) + vendor##device##hook, vendor, device, PCI_ANY_ID, 0, hook) #define DECLARE_PCI_FIXUP_HEADER(vendor, device, hook) \ DECLARE_PCI_FIXUP_SECTION(.pci_fixup_header, \ - vendor##device##hook, vendor, device, hook) + vendor##device##hook, vendor, device, PCI_ANY_ID, 0, hook) #define DECLARE_PCI_FIXUP_FINAL(vendor, device, hook) \ DECLARE_PCI_FIXUP_SECTION(.pci_fixup_final, \ - vendor##device##hook, vendor, device, hook) + vendor##device##hook, vendor, device, PCI_ANY_ID, 0, hook) #define DECLARE_PCI_FIXUP_ENABLE(vendor, device, hook) \ DECLARE_PCI_FIXUP_SECTION(.pci_fixup_enable, \ - vendor##device##hook, vendor, device, hook) + vendor##device##hook, vendor, device, PCI_ANY_ID, 0, hook) #define DECLARE_PCI_FIXUP_RESUME(vendor, device, hook) \ DECLARE_PCI_FIXUP_SECTION(.pci_fixup_resume, \ - resume##vendor##device##hook, vendor, device, hook) + resume##vendor##device##hook, vendor, device, \ + PCI_ANY_ID, 0, hook) #define DECLARE_PCI_FIXUP_RESUME_EARLY(vendor, device, hook) \ DECLARE_PCI_FIXUP_SECTION(.pci_fixup_resume_early, \ - resume_early##vendor##device##hook, vendor, device, hook) + resume_early##vendor##device##hook, vendor, device, \ + PCI_ANY_ID, 0, hook) #define DECLARE_PCI_FIXUP_SUSPEND(vendor, device, hook) \ DECLARE_PCI_FIXUP_SECTION(.pci_fixup_suspend, \ - suspend##vendor##device##hook, vendor, device, hook) + suspend##vendor##device##hook, vendor, device, \ + PCI_ANY_ID, 0, hook) #ifdef CONFIG_PCI_QUIRKS void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev); -- cgit v1.2.3 From 591ad7feaec5417681b4112f8df52fc43bb7c92e Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Mon, 27 Feb 2012 22:05:54 +0400 Subject: SUNRPC: move waitq from RPC pipe to RPC inode Currently, wait queue, used for polling of RPC pipe changes from user-space, is a part of RPC pipe. But the pipe data itself can be released on NFS umount prior to dentry-inode pair, connected to it (is case of this pair is open by some process). This is not a problem for almost all pipe users, because all PipeFS file operations checks pipe reference prior to using it. Except evenfd. This thing registers itself with "poll" file operation and thus has a reference to pipe wait queue. This leads to oopses on destroying eventfd after NFS umount (like rpc_idmapd do) since not pipe data left to the point already. The solution is to wait queue from pipe data to internal RPC inode data. This looks more logical, because this wiat queue used only for user-space processes, which already holds inode reference. Note: upcalls have to get pipe->dentry prior to dereferecing wait queue to make sure, that mount point won't disappear from underneath us. Signed-off-by: Stanislav Kinsbursky Signed-off-by: Trond Myklebust --- include/linux/sunrpc/rpc_pipe_fs.h | 2 +- net/sunrpc/rpc_pipe.c | 39 +++++++++++++++++++++++++------------- 2 files changed, 27 insertions(+), 14 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sunrpc/rpc_pipe_fs.h b/include/linux/sunrpc/rpc_pipe_fs.h index 426ce6eeee66..a7b422b33eda 100644 --- a/include/linux/sunrpc/rpc_pipe_fs.h +++ b/include/linux/sunrpc/rpc_pipe_fs.h @@ -28,7 +28,6 @@ struct rpc_pipe { int pipelen; int nreaders; int nwriters; - wait_queue_head_t waitq; #define RPC_PIPE_WAIT_FOR_OPEN 1 int flags; struct delayed_work queue_timeout; @@ -41,6 +40,7 @@ struct rpc_inode { struct inode vfs_inode; void *private; struct rpc_pipe *pipe; + wait_queue_head_t waitq; }; static inline struct rpc_inode * diff --git a/net/sunrpc/rpc_pipe.c b/net/sunrpc/rpc_pipe.c index b67b2aecc4ff..ac9ee1590739 100644 --- a/net/sunrpc/rpc_pipe.c +++ b/net/sunrpc/rpc_pipe.c @@ -57,7 +57,7 @@ void rpc_pipefs_notifier_unregister(struct notifier_block *nb) } EXPORT_SYMBOL_GPL(rpc_pipefs_notifier_unregister); -static void rpc_purge_list(struct rpc_pipe *pipe, struct list_head *head, +static void rpc_purge_list(wait_queue_head_t *waitq, struct list_head *head, void (*destroy_msg)(struct rpc_pipe_msg *), int err) { struct rpc_pipe_msg *msg; @@ -70,7 +70,7 @@ static void rpc_purge_list(struct rpc_pipe *pipe, struct list_head *head, msg->errno = err; destroy_msg(msg); } while (!list_empty(head)); - wake_up(&pipe->waitq); + wake_up(waitq); } static void @@ -80,6 +80,7 @@ rpc_timeout_upcall_queue(struct work_struct *work) struct rpc_pipe *pipe = container_of(work, struct rpc_pipe, queue_timeout.work); void (*destroy_msg)(struct rpc_pipe_msg *); + struct dentry *dentry; spin_lock(&pipe->lock); destroy_msg = pipe->ops->destroy_msg; @@ -87,8 +88,13 @@ rpc_timeout_upcall_queue(struct work_struct *work) list_splice_init(&pipe->pipe, &free_list); pipe->pipelen = 0; } + dentry = dget(pipe->dentry); spin_unlock(&pipe->lock); - rpc_purge_list(pipe, &free_list, destroy_msg, -ETIMEDOUT); + if (dentry) { + rpc_purge_list(&RPC_I(dentry->d_inode)->waitq, + &free_list, destroy_msg, -ETIMEDOUT); + dput(dentry); + } } ssize_t rpc_pipe_generic_upcall(struct file *filp, struct rpc_pipe_msg *msg, @@ -125,6 +131,7 @@ int rpc_queue_upcall(struct rpc_pipe *pipe, struct rpc_pipe_msg *msg) { int res = -EPIPE; + struct dentry *dentry; spin_lock(&pipe->lock); if (pipe->nreaders) { @@ -140,8 +147,12 @@ rpc_queue_upcall(struct rpc_pipe *pipe, struct rpc_pipe_msg *msg) pipe->pipelen += msg->len; res = 0; } + dentry = dget(pipe->dentry); spin_unlock(&pipe->lock); - wake_up(&pipe->waitq); + if (dentry) { + wake_up(&RPC_I(dentry->d_inode)->waitq); + dput(dentry); + } return res; } EXPORT_SYMBOL_GPL(rpc_queue_upcall); @@ -168,7 +179,7 @@ rpc_close_pipes(struct inode *inode) pipe->pipelen = 0; pipe->dentry = NULL; spin_unlock(&pipe->lock); - rpc_purge_list(pipe, &free_list, pipe->ops->destroy_msg, -EPIPE); + rpc_purge_list(&RPC_I(inode)->waitq, &free_list, pipe->ops->destroy_msg, -EPIPE); pipe->nwriters = 0; if (need_release && pipe->ops->release_pipe) pipe->ops->release_pipe(inode); @@ -257,7 +268,7 @@ rpc_pipe_release(struct inode *inode, struct file *filp) list_splice_init(&pipe->pipe, &free_list); pipe->pipelen = 0; spin_unlock(&pipe->lock); - rpc_purge_list(pipe, &free_list, + rpc_purge_list(&RPC_I(inode)->waitq, &free_list, pipe->ops->destroy_msg, -EAGAIN); } } @@ -330,16 +341,18 @@ rpc_pipe_write(struct file *filp, const char __user *buf, size_t len, loff_t *of static unsigned int rpc_pipe_poll(struct file *filp, struct poll_table_struct *wait) { - struct rpc_pipe *pipe = RPC_I(filp->f_path.dentry->d_inode)->pipe; - unsigned int mask = 0; + struct inode *inode = filp->f_path.dentry->d_inode; + struct rpc_inode *rpci = RPC_I(inode); + unsigned int mask = POLLOUT | POLLWRNORM; - poll_wait(filp, &pipe->waitq, wait); + poll_wait(filp, &rpci->waitq, wait); - mask = POLLOUT | POLLWRNORM; - if (pipe->dentry == NULL) + mutex_lock(&inode->i_mutex); + if (rpci->pipe == NULL) mask |= POLLERR | POLLHUP; - if (filp->private_data || !list_empty(&pipe->pipe)) + else if (filp->private_data || !list_empty(&rpci->pipe->pipe)) mask |= POLLIN | POLLRDNORM; + mutex_unlock(&inode->i_mutex); return mask; } @@ -543,7 +556,6 @@ init_pipe(struct rpc_pipe *pipe) INIT_LIST_HEAD(&pipe->in_downcall); INIT_LIST_HEAD(&pipe->pipe); pipe->pipelen = 0; - init_waitqueue_head(&pipe->waitq); INIT_DELAYED_WORK(&pipe->queue_timeout, rpc_timeout_upcall_queue); pipe->ops = NULL; @@ -1165,6 +1177,7 @@ init_once(void *foo) inode_init_once(&rpci->vfs_inode); rpci->private = NULL; rpci->pipe = NULL; + init_waitqueue_head(&rpci->waitq); } int register_rpc_pipefs(void) -- cgit v1.2.3 From 210647af897af8ef2d00828aa2a6b1b42206aae6 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Sat, 25 Feb 2012 13:54:20 -0800 Subject: PCI: Rename pci_remove_bus_device to pci_stop_and_remove_bus_device The old pci_remove_bus_device actually did stop and remove. Make the name reflect that to reduce confusion. This patch is done by sed scripts and changes back some incorrect __pci_remove_bus_device changes. Suggested-by: Jesse Barnes Signed-off-by: Yinghai Lu Signed-off-by: Jesse Barnes --- arch/arm/mach-ixp2000/ixdp2400.c | 4 ++-- arch/arm/mach-ixp2000/ixdp2800.c | 4 ++-- arch/arm/mach-ixp2000/ixdp2x00.c | 4 ++-- arch/powerpc/platforms/pseries/pci_dlpar.c | 2 +- drivers/message/fusion/mptbase.c | 2 +- drivers/pci/hotplug/acpiphp_glue.c | 4 ++-- drivers/pci/hotplug/cpci_hotplug_pci.c | 2 +- drivers/pci/hotplug/cpqphp_pci.c | 2 +- drivers/pci/hotplug/fakephp.c | 2 +- drivers/pci/hotplug/ibmphp_core.c | 2 +- drivers/pci/hotplug/pciehp_pci.c | 2 +- drivers/pci/hotplug/rpadlpar_core.c | 2 +- drivers/pci/hotplug/sgi_hotplug.c | 2 +- drivers/pci/hotplug/shpchp_pci.c | 2 +- drivers/pci/iov.c | 4 ++-- drivers/pci/pci-sysfs.c | 2 +- drivers/pci/remove.c | 8 ++++---- drivers/pci/xen-pcifront.c | 4 ++-- drivers/platform/x86/asus-wmi.c | 2 +- drivers/platform/x86/eeepc-laptop.c | 2 +- drivers/scsi/mpt2sas/mpt2sas_base.c | 2 +- include/linux/pci.h | 2 +- 22 files changed, 31 insertions(+), 31 deletions(-) (limited to 'include/linux') diff --git a/arch/arm/mach-ixp2000/ixdp2400.c b/arch/arm/mach-ixp2000/ixdp2400.c index f53e911ec94a..d519944653ad 100644 --- a/arch/arm/mach-ixp2000/ixdp2400.c +++ b/arch/arm/mach-ixp2000/ixdp2400.c @@ -134,11 +134,11 @@ static void ixdp2400_pci_postinit(void) if (ixdp2x00_master_npu()) { dev = pci_get_bus_and_slot(1, IXDP2400_SLAVE_ENET_DEVFN); - pci_remove_bus_device(dev); + pci_stop_and_remove_bus_device(dev); pci_dev_put(dev); } else { dev = pci_get_bus_and_slot(1, IXDP2400_MASTER_ENET_DEVFN); - pci_remove_bus_device(dev); + pci_stop_and_remove_bus_device(dev); pci_dev_put(dev); ixdp2x00_slave_pci_postinit(); diff --git a/arch/arm/mach-ixp2000/ixdp2800.c b/arch/arm/mach-ixp2000/ixdp2800.c index a2e7c393e74f..b415febd2025 100644 --- a/arch/arm/mach-ixp2000/ixdp2800.c +++ b/arch/arm/mach-ixp2000/ixdp2800.c @@ -262,14 +262,14 @@ int __init ixdp2800_pci_init(void) pci_common_init(&ixdp2800_pci); if (ixdp2x00_master_npu()) { dev = pci_get_bus_and_slot(1, IXDP2800_SLAVE_ENET_DEVFN); - pci_remove_bus_device(dev); + pci_stop_and_remove_bus_device(dev); pci_dev_put(dev); ixdp2800_master_enable_slave(); ixdp2800_master_wait_for_slave_bus_scan(); } else { dev = pci_get_bus_and_slot(1, IXDP2800_MASTER_ENET_DEVFN); - pci_remove_bus_device(dev); + pci_stop_and_remove_bus_device(dev); pci_dev_put(dev); } } diff --git a/arch/arm/mach-ixp2000/ixdp2x00.c b/arch/arm/mach-ixp2000/ixdp2x00.c index 634b6c852f68..dd9838299068 100644 --- a/arch/arm/mach-ixp2000/ixdp2x00.c +++ b/arch/arm/mach-ixp2000/ixdp2x00.c @@ -239,12 +239,12 @@ void ixdp2x00_slave_pci_postinit(void) * Remove PMC device is there is one */ if((dev = pci_get_bus_and_slot(1, IXDP2X00_PMC_DEVFN))) { - pci_remove_bus_device(dev); + pci_stop_and_remove_bus_device(dev); pci_dev_put(dev); } dev = pci_get_bus_and_slot(0, IXDP2X00_21555_DEVFN); - pci_remove_bus_device(dev); + pci_stop_and_remove_bus_device(dev); pci_dev_put(dev); } diff --git a/arch/powerpc/platforms/pseries/pci_dlpar.c b/arch/powerpc/platforms/pseries/pci_dlpar.c index 55d4ec1bd1ac..200a1ca2528e 100644 --- a/arch/powerpc/platforms/pseries/pci_dlpar.c +++ b/arch/powerpc/platforms/pseries/pci_dlpar.c @@ -84,7 +84,7 @@ void pcibios_remove_pci_devices(struct pci_bus *bus) list_for_each_entry_safe(dev, tmp, &bus->devices, bus_list) { pr_debug(" * Removing %s...\n", pci_name(dev)); eeh_remove_bus_device(dev); - pci_remove_bus_device(dev); + pci_stop_and_remove_bus_device(dev); } } EXPORT_SYMBOL_GPL(pcibios_remove_pci_devices); diff --git a/drivers/message/fusion/mptbase.c b/drivers/message/fusion/mptbase.c index a7dc4672d996..a5c591ffe395 100644 --- a/drivers/message/fusion/mptbase.c +++ b/drivers/message/fusion/mptbase.c @@ -346,7 +346,7 @@ static int mpt_remove_dead_ioc_func(void *arg) if ((pdev == NULL)) return -1; - pci_remove_bus_device(pdev); + pci_stop_and_remove_bus_device(pdev); return 0; } diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 12d070ca7674..fdc34b599e7b 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -910,7 +910,7 @@ static int disable_device(struct acpiphp_slot *slot) disable_bridges(pdev->subordinate); pci_disable_device(pdev); } - pci_remove_bus_device(pdev); + pci_stop_and_remove_bus_device(pdev); pci_dev_put(pdev); } } @@ -1067,7 +1067,7 @@ static void acpiphp_sanitize_bus(struct pci_bus *bus) res->end) { /* Could not assign a required resources * for this device, remove it */ - pci_remove_bus_device(dev); + pci_stop_and_remove_bus_device(dev); break; } } diff --git a/drivers/pci/hotplug/cpci_hotplug_pci.c b/drivers/pci/hotplug/cpci_hotplug_pci.c index 829c327cfb5e..ae853ccd0cd5 100644 --- a/drivers/pci/hotplug/cpci_hotplug_pci.c +++ b/drivers/pci/hotplug/cpci_hotplug_pci.c @@ -341,7 +341,7 @@ int cpci_unconfigure_slot(struct slot* slot) dev = pci_get_slot(slot->bus, PCI_DEVFN(PCI_SLOT(slot->devfn), i)); if (dev) { - pci_remove_bus_device(dev); + pci_stop_and_remove_bus_device(dev); pci_dev_put(dev); } } diff --git a/drivers/pci/hotplug/cpqphp_pci.c b/drivers/pci/hotplug/cpqphp_pci.c index 6173b9a4544e..1c8494021a42 100644 --- a/drivers/pci/hotplug/cpqphp_pci.c +++ b/drivers/pci/hotplug/cpqphp_pci.c @@ -127,7 +127,7 @@ int cpqhp_unconfigure_device(struct pci_func* func) struct pci_dev* temp = pci_get_bus_and_slot(func->bus, PCI_DEVFN(func->device, j)); if (temp) { pci_dev_put(temp); - pci_remove_bus_device(temp); + pci_stop_and_remove_bus_device(temp); } } return 0; diff --git a/drivers/pci/hotplug/fakephp.c b/drivers/pci/hotplug/fakephp.c index 17d10e2e8fb6..a019c9a712be 100644 --- a/drivers/pci/hotplug/fakephp.c +++ b/drivers/pci/hotplug/fakephp.c @@ -40,7 +40,7 @@ static ssize_t legacy_show(struct kobject *kobj, struct attribute *attr, static void remove_callback(void *data) { - pci_remove_bus_device((struct pci_dev *)data); + pci_stop_and_remove_bus_device((struct pci_dev *)data); } static ssize_t legacy_store(struct kobject *kobj, struct attribute *attr, diff --git a/drivers/pci/hotplug/ibmphp_core.c b/drivers/pci/hotplug/ibmphp_core.c index 5506e0e8fbc0..4fda7e6a86a7 100644 --- a/drivers/pci/hotplug/ibmphp_core.c +++ b/drivers/pci/hotplug/ibmphp_core.c @@ -721,7 +721,7 @@ static void ibm_unconfigure_device(struct pci_func *func) for (j = 0; j < 0x08; j++) { temp = pci_get_bus_and_slot(func->busno, (func->device << 3) | j); if (temp) { - pci_remove_bus_device(temp); + pci_stop_and_remove_bus_device(temp); pci_dev_put(temp); } } diff --git a/drivers/pci/hotplug/pciehp_pci.c b/drivers/pci/hotplug/pciehp_pci.c index a4031dfe938e..47d9dc06b109 100644 --- a/drivers/pci/hotplug/pciehp_pci.c +++ b/drivers/pci/hotplug/pciehp_pci.c @@ -141,7 +141,7 @@ int pciehp_unconfigure_device(struct slot *p_slot) break; } } - pci_remove_bus_device(temp); + pci_stop_and_remove_bus_device(temp); /* * Ensure that no new Requests will be generated from * the device. diff --git a/drivers/pci/hotplug/rpadlpar_core.c b/drivers/pci/hotplug/rpadlpar_core.c index c56a9413e1af..1e117c2a3cad 100644 --- a/drivers/pci/hotplug/rpadlpar_core.c +++ b/drivers/pci/hotplug/rpadlpar_core.c @@ -389,7 +389,7 @@ int dlpar_remove_pci_slot(char *drc_name, struct device_node *dn) BUG_ON(!bus->self); pr_debug("PCI: Now removing bridge device %s\n", pci_name(bus->self)); eeh_remove_bus_device(bus->self); - pci_remove_bus_device(bus->self); + pci_stop_and_remove_bus_device(bus->self); return 0; } diff --git a/drivers/pci/hotplug/sgi_hotplug.c b/drivers/pci/hotplug/sgi_hotplug.c index 72d507b6a2aa..de573113c102 100644 --- a/drivers/pci/hotplug/sgi_hotplug.c +++ b/drivers/pci/hotplug/sgi_hotplug.c @@ -554,7 +554,7 @@ static int disable_slot(struct hotplug_slot *bss_hotplug_slot) PCI_FUNC(func))); if (dev) { sn_bus_free_data(dev); - pci_remove_bus_device(dev); + pci_stop_and_remove_bus_device(dev); pci_dev_put(dev); } } diff --git a/drivers/pci/hotplug/shpchp_pci.c b/drivers/pci/hotplug/shpchp_pci.c index a2ccfcd3c298..df7e4bfadae3 100644 --- a/drivers/pci/hotplug/shpchp_pci.c +++ b/drivers/pci/hotplug/shpchp_pci.c @@ -124,7 +124,7 @@ int shpchp_unconfigure_device(struct slot *p_slot) break; } } - pci_remove_bus_device(temp); + pci_stop_and_remove_bus_device(temp); pci_dev_put(temp); } return rc; diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c index 687b3c8e8e3b..6554e1a0f634 100644 --- a/drivers/pci/iov.c +++ b/drivers/pci/iov.c @@ -142,7 +142,7 @@ failed2: failed1: pci_dev_put(dev); mutex_lock(&iov->dev->sriov->lock); - pci_remove_bus_device(virtfn); + pci_stop_and_remove_bus_device(virtfn); virtfn_remove_bus(dev->bus, virtfn_bus(dev, id)); mutex_unlock(&iov->dev->sriov->lock); @@ -182,7 +182,7 @@ static void virtfn_remove(struct pci_dev *dev, int id, int reset) sysfs_remove_link(&virtfn->dev.kobj, "physfn"); mutex_lock(&iov->dev->sriov->lock); - pci_remove_bus_device(virtfn); + pci_stop_and_remove_bus_device(virtfn); virtfn_remove_bus(dev->bus, virtfn_bus(dev, id)); mutex_unlock(&iov->dev->sriov->lock); diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index d9723039e99a..a55e248618cd 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -330,7 +330,7 @@ static void remove_callback(struct device *dev) struct pci_dev *pdev = to_pci_dev(dev); mutex_lock(&pci_remove_rescan_mutex); - pci_remove_bus_device(pdev); + pci_stop_and_remove_bus_device(pdev); mutex_unlock(&pci_remove_rescan_mutex); } diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c index 82f8ae572703..7abe67b45cc8 100644 --- a/drivers/pci/remove.c +++ b/drivers/pci/remove.c @@ -79,7 +79,7 @@ EXPORT_SYMBOL(pci_remove_bus); static void __pci_remove_behind_bridge(struct pci_dev *dev); /** - * pci_remove_bus_device - remove a PCI device and any children + * pci_stop_and_remove_bus_device - remove a PCI device and any children * @dev: the device to remove * * Remove a PCI device from the device lists, informing the drivers @@ -102,7 +102,7 @@ static void __pci_remove_bus_device(struct pci_dev *dev) pci_destroy_dev(dev); } -void pci_remove_bus_device(struct pci_dev *dev) +void pci_stop_and_remove_bus_device(struct pci_dev *dev) { pci_stop_bus_device(dev); __pci_remove_bus_device(dev); @@ -145,7 +145,7 @@ static void pci_stop_bus_devices(struct pci_bus *bus) struct list_head *l, *n; /* - * VFs could be removed by pci_remove_bus_device() in the + * VFs could be removed by pci_stop_and_remove_bus_device() in the * pci_stop_bus_devices() code path for PF. * aka, bus->devices get updated in the process. * but VFs are inserted after PFs when SRIOV is enabled for PF, @@ -174,6 +174,6 @@ void pci_stop_bus_device(struct pci_dev *dev) pci_stop_dev(dev); } -EXPORT_SYMBOL(pci_remove_bus_device); +EXPORT_SYMBOL(pci_stop_and_remove_bus_device); EXPORT_SYMBOL(pci_remove_behind_bridge); EXPORT_SYMBOL_GPL(pci_stop_bus_device); diff --git a/drivers/pci/xen-pcifront.c b/drivers/pci/xen-pcifront.c index 7cf3d2fcf56a..fb8a39c7c3d6 100644 --- a/drivers/pci/xen-pcifront.c +++ b/drivers/pci/xen-pcifront.c @@ -544,7 +544,7 @@ static void free_root_bus_devs(struct pci_bus *bus) dev = container_of(bus->devices.next, struct pci_dev, bus_list); dev_dbg(&dev->dev, "removing device\n"); - pci_remove_bus_device(dev); + pci_stop_and_remove_bus_device(dev); } } @@ -1045,7 +1045,7 @@ static int pcifront_detach_devices(struct pcifront_device *pdev) domain, bus, slot, func); continue; } - pci_remove_bus_device(pci_dev); + pci_stop_and_remove_bus_device(pci_dev); pci_dev_put(pci_dev); dev_dbg(&pdev->xdev->dev, diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 72d731c21d45..9929246895de 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -571,7 +571,7 @@ static void asus_rfkill_hotplug(struct asus_wmi *asus) } else { dev = pci_get_slot(bus, 0); if (dev) { - pci_remove_bus_device(dev); + pci_stop_and_remove_bus_device(dev); pci_dev_put(dev); } } diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index ea44abd8df48..d9a9e2bedb30 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c @@ -646,7 +646,7 @@ static void eeepc_rfkill_hotplug(struct eeepc_laptop *eeepc, acpi_handle handle) } else { dev = pci_get_slot(bus, 0); if (dev) { - pci_remove_bus_device(dev); + pci_stop_and_remove_bus_device(dev); pci_dev_put(dev); } } diff --git a/drivers/scsi/mpt2sas/mpt2sas_base.c b/drivers/scsi/mpt2sas/mpt2sas_base.c index 0b2c95583660..c7d5a921a430 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_base.c +++ b/drivers/scsi/mpt2sas/mpt2sas_base.c @@ -132,7 +132,7 @@ static int mpt2sas_remove_dead_ioc_func(void *arg) pdev = ioc->pdev; if ((pdev == NULL)) return -1; - pci_remove_bus_device(pdev); + pci_stop_and_remove_bus_device(pdev); return 0; } diff --git a/include/linux/pci.h b/include/linux/pci.h index 74a20bdc93f6..a4c552d9908e 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -686,7 +686,7 @@ u8 pci_common_swizzle(struct pci_dev *dev, u8 *pinp); extern struct pci_dev *pci_dev_get(struct pci_dev *dev); extern void pci_dev_put(struct pci_dev *dev); extern void pci_remove_bus(struct pci_bus *b); -extern void pci_remove_bus_device(struct pci_dev *dev); +extern void pci_stop_and_remove_bus_device(struct pci_dev *dev); extern void pci_stop_bus_device(struct pci_dev *dev); void pci_setup_cardbus(struct pci_bus *bus); extern void pci_sort_breadthfirst(void); -- cgit v1.2.3 From 6754b9e9c33502223db066de50dda8a876f70c2c Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Sat, 25 Feb 2012 13:54:22 -0800 Subject: PCI: Rename pci_remove_behind_bridge to pci_stop_and_remove_behind_bridge The old pci_remove_behind_bridge actually do stop and remove. Make the name reflect that to reduce confusion. Suggested-by: Jesse Barnes Signed-off-by: Yinghai Lu Signed-off-by: Jesse Barnes --- drivers/pci/remove.c | 7 ++++--- drivers/pcmcia/cardbus.c | 2 +- include/linux/pci.h | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c index 7abe67b45cc8..bd2be1c4c668 100644 --- a/drivers/pci/remove.c +++ b/drivers/pci/remove.c @@ -127,14 +127,15 @@ static void pci_stop_behind_bridge(struct pci_dev *dev) } /** - * pci_remove_behind_bridge - remove all devices behind a PCI bridge + * pci_stop_and_remove_behind_bridge - stop and remove all devices behind + * a PCI bridge * @dev: PCI bridge device * * Remove all devices on the bus, except for the parent bridge. * This also removes any child buses, and any devices they may * contain in a depth-first manner. */ -void pci_remove_behind_bridge(struct pci_dev *dev) +void pci_stop_and_remove_behind_bridge(struct pci_dev *dev) { pci_stop_behind_bridge(dev); __pci_remove_behind_bridge(dev); @@ -175,5 +176,5 @@ void pci_stop_bus_device(struct pci_dev *dev) } EXPORT_SYMBOL(pci_stop_and_remove_bus_device); -EXPORT_SYMBOL(pci_remove_behind_bridge); +EXPORT_SYMBOL(pci_stop_and_remove_behind_bridge); EXPORT_SYMBOL_GPL(pci_stop_bus_device); diff --git a/drivers/pcmcia/cardbus.c b/drivers/pcmcia/cardbus.c index 9a58862f1401..6e75153c5b4f 100644 --- a/drivers/pcmcia/cardbus.c +++ b/drivers/pcmcia/cardbus.c @@ -108,5 +108,5 @@ void cb_free(struct pcmcia_socket *s) struct pci_dev *bridge = s->cb_dev; if (bridge) - pci_remove_behind_bridge(bridge); + pci_stop_and_remove_behind_bridge(bridge); } diff --git a/include/linux/pci.h b/include/linux/pci.h index a4c552d9908e..073ae9d97ad6 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -946,7 +946,7 @@ int __must_check __pci_register_driver(struct pci_driver *, struct module *, __pci_register_driver(driver, THIS_MODULE, KBUILD_MODNAME) void pci_unregister_driver(struct pci_driver *dev); -void pci_remove_behind_bridge(struct pci_dev *dev); +void pci_stop_and_remove_behind_bridge(struct pci_dev *dev); struct pci_driver *pci_dev_driver(const struct pci_dev *dev); int pci_add_dynid(struct pci_driver *drv, unsigned int vendor, unsigned int device, -- cgit v1.2.3 From 6b22cf3f35fd332e4cc2c1b27056920b3643667a Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Sat, 25 Feb 2012 13:54:21 -0800 Subject: PCI: export __pci_remove_bus_device Don't switch to pci_remove_bus_device yet, keep the __ prefix for now (the behavior is still the same: remove without stopping first). This allows other out of tree users or pending patches to get notified from compiler warning. Signed-off-by: Yinghai Lu Signed-off-by: Jesse Barnes --- drivers/pci/remove.c | 4 +++- include/linux/pci.h | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c index bd2be1c4c668..fd77e2bde2e8 100644 --- a/drivers/pci/remove.c +++ b/drivers/pci/remove.c @@ -90,7 +90,7 @@ static void __pci_remove_behind_bridge(struct pci_dev *dev); * device lists, remove the /proc entry, and notify userspace * (/sbin/hotplug). */ -static void __pci_remove_bus_device(struct pci_dev *dev) +void __pci_remove_bus_device(struct pci_dev *dev) { if (dev->subordinate) { struct pci_bus *b = dev->subordinate; @@ -102,6 +102,8 @@ static void __pci_remove_bus_device(struct pci_dev *dev) pci_destroy_dev(dev); } +EXPORT_SYMBOL(__pci_remove_bus_device); + void pci_stop_and_remove_bus_device(struct pci_dev *dev) { pci_stop_bus_device(dev); diff --git a/include/linux/pci.h b/include/linux/pci.h index 073ae9d97ad6..5584aac96e28 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -686,6 +686,7 @@ u8 pci_common_swizzle(struct pci_dev *dev, u8 *pinp); extern struct pci_dev *pci_dev_get(struct pci_dev *dev); extern void pci_dev_put(struct pci_dev *dev); extern void pci_remove_bus(struct pci_bus *b); +extern void __pci_remove_bus_device(struct pci_dev *dev); extern void pci_stop_and_remove_bus_device(struct pci_dev *dev); extern void pci_stop_bus_device(struct pci_dev *dev); void pci_setup_cardbus(struct pci_bus *bus); -- cgit v1.2.3 From c7361ae1e7a5a5395693f1f978af032c41cdb10d Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Fri, 20 Jan 2012 15:37:44 -0300 Subject: [media] V4L: Add JPEG compression control class The V4L2_CID_JPEG_CLASS control class is intended to expose various adjustable parameters of JPEG encoders and decoders. Following controls are defined: - V4L2_CID_JPEG_CHROMA_SUBSAMPLING, - V4L2_CID_JPEG_RESTART_INTERVAL, - V4L2_CID_JPEG_COMPRESSION_QUALITY, - V4L2_CID_JPEG_ACTIVE_MARKER. This covers only a part of relevant standard specifications. More controls should be added in future if required. The purpose of V4L2_CID_JPEG_CLASS class is also to replace some functionality covered by VIDIOC_S/G_JPEGCOMP ioctls, i.e. the JPEG markers presence and compression quality control. The applications and drivers should switch from the ioctl to control based API, as described in the subsequent patches for the Media API DocBook. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/v4l2-ctrls.c | 24 ++++++++++++++++++++++++ include/linux/videodev2.h | 24 ++++++++++++++++++++++++ 2 files changed, 48 insertions(+) (limited to 'include/linux') diff --git a/drivers/media/video/v4l2-ctrls.c b/drivers/media/video/v4l2-ctrls.c index 909117203610..e6c7c8e8cc4e 100644 --- a/drivers/media/video/v4l2-ctrls.c +++ b/drivers/media/video/v4l2-ctrls.c @@ -362,6 +362,16 @@ const char * const *v4l2_ctrl_get_menu(u32 id) NULL, }; + static const char * const jpeg_chroma_subsampling[] = { + "4:4:4", + "4:2:2", + "4:2:0", + "4:1:1", + "4:1:0", + "Gray", + NULL, + }; + switch (id) { case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: return mpeg_audio_sampling_freq; @@ -426,6 +436,9 @@ const char * const *v4l2_ctrl_get_menu(u32 id) return mpeg_mpeg4_level; case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE: return mpeg4_profile; + case V4L2_CID_JPEG_CHROMA_SUBSAMPLING: + return jpeg_chroma_subsampling; + default: return NULL; } @@ -623,6 +636,14 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_FLASH_CHARGE: return "Charge"; case V4L2_CID_FLASH_READY: return "Ready to Strobe"; + /* JPEG encoder controls */ + /* Keep the order of the 'case's the same as in videodev2.h! */ + case V4L2_CID_JPEG_CLASS: return "JPEG Compression Controls"; + case V4L2_CID_JPEG_CHROMA_SUBSAMPLING: return "Chroma Subsampling"; + case V4L2_CID_JPEG_RESTART_INTERVAL: return "Restart Interval"; + case V4L2_CID_JPEG_COMPRESSION_QUALITY: return "Compression Quality"; + case V4L2_CID_JPEG_ACTIVE_MARKER: return "Active Markers"; + default: return NULL; } @@ -711,6 +732,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC: case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL: case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE: + case V4L2_CID_JPEG_CHROMA_SUBSAMPLING: *type = V4L2_CTRL_TYPE_MENU; break; case V4L2_CID_RDS_TX_PS_NAME: @@ -722,6 +744,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_MPEG_CLASS: case V4L2_CID_FM_TX_CLASS: case V4L2_CID_FLASH_CLASS: + case V4L2_CID_JPEG_CLASS: *type = V4L2_CTRL_TYPE_CTRL_CLASS; /* You can neither read not write these */ *flags |= V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_WRITE_ONLY; @@ -735,6 +758,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, *max = 0xFFFFFF; break; case V4L2_CID_FLASH_FAULT: + case V4L2_CID_JPEG_ACTIVE_MARKER: *type = V4L2_CTRL_TYPE_BITMASK; break; case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE: diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index b739d7d6f7e7..3fab6cacccbe 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -1136,6 +1136,7 @@ struct v4l2_ext_controls { #define V4L2_CTRL_CLASS_CAMERA 0x009a0000 /* Camera class controls */ #define V4L2_CTRL_CLASS_FM_TX 0x009b0000 /* FM Modulator control class */ #define V4L2_CTRL_CLASS_FLASH 0x009c0000 /* Camera flash controls */ +#define V4L2_CTRL_CLASS_JPEG 0x009d0000 /* JPEG-compression controls */ #define V4L2_CTRL_ID_MASK (0x0fffffff) #define V4L2_CTRL_ID2CLASS(id) ((id) & 0x0fff0000UL) @@ -1758,6 +1759,29 @@ enum v4l2_flash_strobe_source { #define V4L2_CID_FLASH_CHARGE (V4L2_CID_FLASH_CLASS_BASE + 11) #define V4L2_CID_FLASH_READY (V4L2_CID_FLASH_CLASS_BASE + 12) +/* JPEG-class control IDs defined by V4L2 */ +#define V4L2_CID_JPEG_CLASS_BASE (V4L2_CTRL_CLASS_JPEG | 0x900) +#define V4L2_CID_JPEG_CLASS (V4L2_CTRL_CLASS_JPEG | 1) + +#define V4L2_CID_JPEG_CHROMA_SUBSAMPLING (V4L2_CID_JPEG_CLASS_BASE + 1) +enum v4l2_jpeg_chroma_subsampling { + V4L2_JPEG_CHROMA_SUBSAMPLING_444 = 0, + V4L2_JPEG_CHROMA_SUBSAMPLING_422 = 1, + V4L2_JPEG_CHROMA_SUBSAMPLING_420 = 2, + V4L2_JPEG_CHROMA_SUBSAMPLING_411 = 3, + V4L2_JPEG_CHROMA_SUBSAMPLING_410 = 4, + V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY = 5, +}; +#define V4L2_CID_JPEG_RESTART_INTERVAL (V4L2_CID_JPEG_CLASS_BASE + 2) +#define V4L2_CID_JPEG_COMPRESSION_QUALITY (V4L2_CID_JPEG_CLASS_BASE + 3) + +#define V4L2_CID_JPEG_ACTIVE_MARKER (V4L2_CID_JPEG_CLASS_BASE + 4) +#define V4L2_JPEG_ACTIVE_MARKER_APP0 (1 << 0) +#define V4L2_JPEG_ACTIVE_MARKER_APP1 (1 << 1) +#define V4L2_JPEG_ACTIVE_MARKER_COM (1 << 16) +#define V4L2_JPEG_ACTIVE_MARKER_DQT (1 << 17) +#define V4L2_JPEG_ACTIVE_MARKER_DHT (1 << 18) + /* * T U N I N G */ -- cgit v1.2.3 From 4ebc1b4b0004b45e280940db81888a7863d3a01d Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Fri, 20 Jan 2012 18:20:37 -0500 Subject: spinlock: macroize assert_spin_locked to avoid bug.h dependency In spinlock_api_smp.h we find a define for assert_raw_spin_locked [which uses BUG_ON]. Then assert_spin_locked (as an inline) uses it, meaning we need bug.h But rather than put linux/bug.h in such a highly used file like spinlock.h, we can just make the un-raw version also a macro. Then the required bug.h presence is limited just to those few files who are actually doing the assert testing. Signed-off-by: Paul Gortmaker CC: Thomas Gleixner --- include/linux/spinlock.h | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'include/linux') diff --git a/include/linux/spinlock.h b/include/linux/spinlock.h index 7df6c17b0281..363239087263 100644 --- a/include/linux/spinlock.h +++ b/include/linux/spinlock.h @@ -375,10 +375,7 @@ static inline int spin_can_lock(spinlock_t *lock) return raw_spin_can_lock(&lock->rlock); } -static inline void assert_spin_locked(spinlock_t *lock) -{ - assert_raw_spin_locked(&lock->rlock); -} +#define assert_spin_locked(lock) assert_raw_spin_locked(&(lock)->rlock) /* * Pull the atomic_t declaration: -- cgit v1.2.3 From 806d314325812fb8ffe7059bd84a23d334350c21 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Thu, 23 Feb 2012 17:04:39 -0700 Subject: pinctrl: re-order struct pinctrl_map The lookup key in struct pinctrl_map is (.dev_name, .name). Re-order the struct definition to put the lookup key fields first, and the result values afterwards. To me at least, this slightly better reflects the lookup process. Update the documentation in a similar fashion. Note: PIN_MAP*() macros aren't updated; I plan to update this once later when enhancing the mapping table format to support pin config to reduce churn. Signed-off-by: Stephen Warren Acked-by: Dong Aisheng [Rebased for cherry-picking] Signed-off-by: Linus Walleij --- Documentation/pinctrl.txt | 24 ++++++++++++------------ include/linux/pinctrl/machine.h | 10 +++++----- 2 files changed, 17 insertions(+), 17 deletions(-) (limited to 'include/linux') diff --git a/Documentation/pinctrl.txt b/Documentation/pinctrl.txt index fa9163af34f6..5e314cecab77 100644 --- a/Documentation/pinctrl.txt +++ b/Documentation/pinctrl.txt @@ -781,19 +781,19 @@ spi on the second function mapping: static const struct pinctrl_map __initdata mapping[] = { { + .dev_name = "foo-spi.0", .ctrl_dev_name = "pinctrl-foo", .function = "spi0", - .dev_name = "foo-spi.0", }, { + .dev_name = "foo-i2c.0", .ctrl_dev_name = "pinctrl-foo", .function = "i2c0", - .dev_name = "foo-i2c.0", }, { + .dev_name = "foo-mmc.0", .ctrl_dev_name = "pinctrl-foo", .function = "mmc0", - .dev_name = "foo-mmc.0", }, }; @@ -826,18 +826,18 @@ As it is possible to map a function to different groups of pins an optional ... { + .dev_name = "foo-spi.0", .name = "spi0-pos-A", .ctrl_dev_name = "pinctrl-foo", .function = "spi0", .group = "spi0_0_grp", - .dev_name = "foo-spi.0", }, { + .dev_name = "foo-spi.0", .name = "spi0-pos-B", .ctrl_dev_name = "pinctrl-foo", .function = "spi0", .group = "spi0_1_grp", - .dev_name = "foo-spi.0", }, ... @@ -852,45 +852,45 @@ case), we define a mapping like this: ... { + .dev_name = "foo-mmc.0", .name = "2bit" .ctrl_dev_name = "pinctrl-foo", .function = "mmc0", .group = "mmc0_1_grp", - .dev_name = "foo-mmc.0", }, { + .dev_name = "foo-mmc.0", .name = "4bit" .ctrl_dev_name = "pinctrl-foo", .function = "mmc0", .group = "mmc0_1_grp", - .dev_name = "foo-mmc.0", }, { + .dev_name = "foo-mmc.0", .name = "4bit" .ctrl_dev_name = "pinctrl-foo", .function = "mmc0", .group = "mmc0_2_grp", - .dev_name = "foo-mmc.0", }, { + .dev_name = "foo-mmc.0", .name = "8bit" .ctrl_dev_name = "pinctrl-foo", .group = "mmc0_1_grp", - .dev_name = "foo-mmc.0", }, { + .dev_name = "foo-mmc.0", .name = "8bit" .ctrl_dev_name = "pinctrl-foo", .function = "mmc0", .group = "mmc0_2_grp", - .dev_name = "foo-mmc.0", }, { + .dev_name = "foo-mmc.0", .name = "8bit" .ctrl_dev_name = "pinctrl-foo", .function = "mmc0", .group = "mmc0_3_grp", - .dev_name = "foo-mmc.0", }, ... @@ -988,10 +988,10 @@ This is enabled by simply setting the .dev_name field in the map to the name of the pin controller itself, like this: { + .dev_name = "pinctrl-foo", .name = "POWERMAP" .ctrl_dev_name = "pinctrl-foo", .function = "power_func", - .dev_name = "pinctrl-foo", }, Since it may be common to request the core to hog a few always-applicable diff --git a/include/linux/pinctrl/machine.h b/include/linux/pinctrl/machine.h index 400f1926b234..73fbb2745301 100644 --- a/include/linux/pinctrl/machine.h +++ b/include/linux/pinctrl/machine.h @@ -14,6 +14,10 @@ /** * struct pinctrl_map - boards/machines shall provide this map for devices + * @dev_name: the name of the device using this specific mapping, the name + * must be the same as in your struct device*. If this name is set to the + * same name as the pin controllers own dev_name(), the map entry will be + * hogged by the driver itself upon registration * @name: the name of this specific map entry for the particular machine. * This is the second parameter passed to pinmux_get() when you want * to have several mappings to the same device @@ -25,17 +29,13 @@ * @group: sometimes a function can map to different pin groups, so this * selects a certain specific pin group to activate for the function, if * left as NULL, the first applicable group will be used - * @dev_name: the name of the device using this specific mapping, the name - * must be the same as in your struct device*. If this name is set to the - * same name as the pin controllers own dev_name(), the map entry will be - * hogged by the driver itself upon registration */ struct pinctrl_map { + const char *dev_name; const char *name; const char *ctrl_dev_name; const char *function; const char *group; - const char *dev_name; }; /* -- cgit v1.2.3 From a313f9f565d84f2fcc81c818a6b0baaae752a821 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 23 Feb 2012 19:49:43 +0000 Subject: regmap: Add stub for regcache_sync_region() Added on another branch. Signed-off-by: Mark Brown --- include/linux/regmap.h | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'include/linux') diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 42c69e75b131..33d5f1d9f882 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -280,6 +280,13 @@ static inline int regcache_sync(struct regmap *map) return -EINVAL; } +static inline int regcache_sync_region(struct regmap *map, unsigned int min, + unsigned int max) +{ + WARN_ONCE(1, "regmap API is disabled"); + return -EINVAL; +} + static inline void regcache_cache_only(struct regmap *map, bool enable) { WARN_ONCE(1, "regmap API is disabled"); -- cgit v1.2.3 From 8e315a7b0c082c6743a6636ead5674a2265638d3 Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Thu, 19 Jan 2012 18:44:49 +0100 Subject: ARM: at91/tc/clocksource: Add 32 bit variant to Timer Counter Some SoC have a 32 bit variant of Timer Counter Blocks. We do not need the chaining of two 16 bit counters anymore for them. The SoC nature is deduced from the device tree "compatible" string. For non-device-tree configurations, backward compatibility is maintained by using the default 16 bit counter configuration. This patch addresses both the atmel_tclib and its user: tcb_clksrc clocksource. Signed-off-by: Nicolas Ferre Acked-by: Jean-Christophe PLAGNIOL-VILLARD Acked-by: Grant Likely --- drivers/clocksource/tcb_clksrc.c | 90 +++++++++++++++++++++++++++------------- drivers/misc/atmel_tclib.c | 20 +++++++++ include/linux/atmel_tc.h | 10 +++++ 3 files changed, 92 insertions(+), 28 deletions(-) (limited to 'include/linux') diff --git a/drivers/clocksource/tcb_clksrc.c b/drivers/clocksource/tcb_clksrc.c index 55d0f95f82f9..32cb929b8eb6 100644 --- a/drivers/clocksource/tcb_clksrc.c +++ b/drivers/clocksource/tcb_clksrc.c @@ -19,6 +19,8 @@ * - Two channels combine to create a free-running 32 bit counter * with a base rate of 5+ MHz, packaged as a clocksource (with * resolution better than 200 nsec). + * - Some chips support 32 bit counter. A single channel is used for + * this 32 bit free-running counter. the second channel is not used. * * - The third channel may be used to provide a 16-bit clockevent * source, used in either periodic or oneshot mode. This runs @@ -54,6 +56,11 @@ static cycle_t tc_get_cycles(struct clocksource *cs) return (upper << 16) | lower; } +static cycle_t tc_get_cycles32(struct clocksource *cs) +{ + return __raw_readl(tcaddr + ATMEL_TC_REG(0, CV)); +} + static struct clocksource clksrc = { .name = "tcb_clksrc", .rating = 200, @@ -209,6 +216,48 @@ static void __init setup_clkevents(struct atmel_tc *tc, int clk32k_divisor_idx) #endif +static void __init tcb_setup_dual_chan(struct atmel_tc *tc, int mck_divisor_idx) +{ + /* channel 0: waveform mode, input mclk/8, clock TIOA0 on overflow */ + __raw_writel(mck_divisor_idx /* likely divide-by-8 */ + | ATMEL_TC_WAVE + | ATMEL_TC_WAVESEL_UP /* free-run */ + | ATMEL_TC_ACPA_SET /* TIOA0 rises at 0 */ + | ATMEL_TC_ACPC_CLEAR, /* (duty cycle 50%) */ + tcaddr + ATMEL_TC_REG(0, CMR)); + __raw_writel(0x0000, tcaddr + ATMEL_TC_REG(0, RA)); + __raw_writel(0x8000, tcaddr + ATMEL_TC_REG(0, RC)); + __raw_writel(0xff, tcaddr + ATMEL_TC_REG(0, IDR)); /* no irqs */ + __raw_writel(ATMEL_TC_CLKEN, tcaddr + ATMEL_TC_REG(0, CCR)); + + /* channel 1: waveform mode, input TIOA0 */ + __raw_writel(ATMEL_TC_XC1 /* input: TIOA0 */ + | ATMEL_TC_WAVE + | ATMEL_TC_WAVESEL_UP, /* free-run */ + tcaddr + ATMEL_TC_REG(1, CMR)); + __raw_writel(0xff, tcaddr + ATMEL_TC_REG(1, IDR)); /* no irqs */ + __raw_writel(ATMEL_TC_CLKEN, tcaddr + ATMEL_TC_REG(1, CCR)); + + /* chain channel 0 to channel 1*/ + __raw_writel(ATMEL_TC_TC1XC1S_TIOA0, tcaddr + ATMEL_TC_BMR); + /* then reset all the timers */ + __raw_writel(ATMEL_TC_SYNC, tcaddr + ATMEL_TC_BCR); +} + +static void __init tcb_setup_single_chan(struct atmel_tc *tc, int mck_divisor_idx) +{ + /* channel 0: waveform mode, input mclk/8 */ + __raw_writel(mck_divisor_idx /* likely divide-by-8 */ + | ATMEL_TC_WAVE + | ATMEL_TC_WAVESEL_UP, /* free-run */ + tcaddr + ATMEL_TC_REG(0, CMR)); + __raw_writel(0xff, tcaddr + ATMEL_TC_REG(0, IDR)); /* no irqs */ + __raw_writel(ATMEL_TC_CLKEN, tcaddr + ATMEL_TC_REG(0, CCR)); + + /* then reset all the timers */ + __raw_writel(ATMEL_TC_SYNC, tcaddr + ATMEL_TC_BCR); +} + static int __init tcb_clksrc_init(void) { static char bootinfo[] __initdata @@ -260,34 +309,19 @@ static int __init tcb_clksrc_init(void) divided_rate / 1000000, ((divided_rate + 500000) % 1000000) / 1000); - /* tclib will give us three clocks no matter what the - * underlying platform supports. - */ - clk_enable(tc->clk[1]); - - /* channel 0: waveform mode, input mclk/8, clock TIOA0 on overflow */ - __raw_writel(best_divisor_idx /* likely divide-by-8 */ - | ATMEL_TC_WAVE - | ATMEL_TC_WAVESEL_UP /* free-run */ - | ATMEL_TC_ACPA_SET /* TIOA0 rises at 0 */ - | ATMEL_TC_ACPC_CLEAR, /* (duty cycle 50%) */ - tcaddr + ATMEL_TC_REG(0, CMR)); - __raw_writel(0x0000, tcaddr + ATMEL_TC_REG(0, RA)); - __raw_writel(0x8000, tcaddr + ATMEL_TC_REG(0, RC)); - __raw_writel(0xff, tcaddr + ATMEL_TC_REG(0, IDR)); /* no irqs */ - __raw_writel(ATMEL_TC_CLKEN, tcaddr + ATMEL_TC_REG(0, CCR)); - - /* channel 1: waveform mode, input TIOA0 */ - __raw_writel(ATMEL_TC_XC1 /* input: TIOA0 */ - | ATMEL_TC_WAVE - | ATMEL_TC_WAVESEL_UP, /* free-run */ - tcaddr + ATMEL_TC_REG(1, CMR)); - __raw_writel(0xff, tcaddr + ATMEL_TC_REG(1, IDR)); /* no irqs */ - __raw_writel(ATMEL_TC_CLKEN, tcaddr + ATMEL_TC_REG(1, CCR)); - - /* chain channel 0 to channel 1, then reset all the timers */ - __raw_writel(ATMEL_TC_TC1XC1S_TIOA0, tcaddr + ATMEL_TC_BMR); - __raw_writel(ATMEL_TC_SYNC, tcaddr + ATMEL_TC_BCR); + if (tc->tcb_config && tc->tcb_config->counter_width == 32) { + /* use apropriate function to read 32 bit counter */ + clksrc.read = tc_get_cycles32; + /* setup ony channel 0 */ + tcb_setup_single_chan(tc, best_divisor_idx); + } else { + /* tclib will give us three clocks no matter what the + * underlying platform supports. + */ + clk_enable(tc->clk[1]); + /* setup both channel 0 & 1 */ + tcb_setup_dual_chan(tc, best_divisor_idx); + } /* and away we go! */ clocksource_register_hz(&clksrc, divided_rate); diff --git a/drivers/misc/atmel_tclib.c b/drivers/misc/atmel_tclib.c index de6dea7c5d52..c8d8e38d0d8a 100644 --- a/drivers/misc/atmel_tclib.c +++ b/drivers/misc/atmel_tclib.c @@ -114,9 +114,21 @@ void atmel_tc_free(struct atmel_tc *tc) EXPORT_SYMBOL_GPL(atmel_tc_free); #if defined(CONFIG_OF) +static struct atmel_tcb_config tcb_rm9200_config = { + .counter_width = 16, +}; + +static struct atmel_tcb_config tcb_sam9x5_config = { + .counter_width = 32, +}; + static const struct of_device_id atmel_tcb_dt_ids[] = { { .compatible = "atmel,at91rm9200-tcb", + .data = &tcb_rm9200_config, + }, { + .compatible = "atmel,at91sam9x5-tcb", + .data = &tcb_sam9x5_config, }, { /* sentinel */ } @@ -150,6 +162,14 @@ static int __init tc_probe(struct platform_device *pdev) return -EINVAL; } + /* Now take SoC information if available */ + if (pdev->dev.of_node) { + const struct of_device_id *match; + match = of_match_node(atmel_tcb_dt_ids, pdev->dev.of_node); + if (match) + tc->tcb_config = match->data; + } + tc->clk[0] = clk; tc->clk[1] = clk_get(&pdev->dev, "t1_clk"); if (IS_ERR(tc->clk[1])) diff --git a/include/linux/atmel_tc.h b/include/linux/atmel_tc.h index 53ba65e30caa..1d14b1dc1aee 100644 --- a/include/linux/atmel_tc.h +++ b/include/linux/atmel_tc.h @@ -33,11 +33,20 @@ struct clk; +/** + * struct atmel_tcb_config - SoC data for a Timer/Counter Block + * @counter_width: size in bits of a timer counter register + */ +struct atmel_tcb_config { + size_t counter_width; +}; + /** * struct atmel_tc - information about a Timer/Counter Block * @pdev: physical device * @iomem: resource associated with the I/O register * @regs: mapping through which the I/O registers can be accessed + * @tcb_config: configuration data from SoC * @irq: irq for each of the three channels * @clk: internal clock source for each of the three channels * @node: list node, for tclib internal use @@ -54,6 +63,7 @@ struct atmel_tc { struct platform_device *pdev; struct resource *iomem; void __iomem *regs; + struct atmel_tcb_config *tcb_config; int irq[3]; struct clk *clk[3]; struct list_head node; -- cgit v1.2.3 From 8704a1ba4751fc5ffe2289765cb7222b3a1e20d5 Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Thu, 1 Mar 2012 15:57:53 +0100 Subject: iommu/amd: Split amd_iommu_init function The hardware-initializtion part of the AMD IOMMU driver is split out into a seperate function. This function can now be called either from amd_iommu_init() itself or any other place if the hardware needs to be ready earlier. This will be used to implement interrupt remapping for AMD. Signed-off-by: Joerg Roedel --- drivers/iommu/amd_iommu_init.c | 123 ++++++++++++++++++++++++++--------------- include/linux/amd-iommu.h | 2 +- 2 files changed, 80 insertions(+), 45 deletions(-) (limited to 'include/linux') diff --git a/drivers/iommu/amd_iommu_init.c b/drivers/iommu/amd_iommu_init.c index bdea288dc185..75653daf1de1 100644 --- a/drivers/iommu/amd_iommu_init.c +++ b/drivers/iommu/amd_iommu_init.c @@ -1424,10 +1424,40 @@ static struct syscore_ops amd_iommu_syscore_ops = { .resume = amd_iommu_resume, }; +static void __init free_on_init_error(void) +{ + amd_iommu_uninit_devices(); + + free_pages((unsigned long)amd_iommu_pd_alloc_bitmap, + get_order(MAX_DOMAIN_ID/8)); + + free_pages((unsigned long)amd_iommu_rlookup_table, + get_order(rlookup_table_size)); + + free_pages((unsigned long)amd_iommu_alias_table, + get_order(alias_table_size)); + + free_pages((unsigned long)amd_iommu_dev_table, + get_order(dev_table_size)); + + free_iommu_all(); + + free_unity_maps(); + +#ifdef CONFIG_GART_IOMMU + /* + * We failed to initialize the AMD IOMMU - try fallback to GART + * if possible. + */ + gart_iommu_init(); + +#endif +} + /* - * This is the core init function for AMD IOMMU hardware in the system. - * This function is called from the generic x86 DMA layer initialization - * code. + * This is the hardware init function for AMD IOMMU in the system. + * This function is called either from amd_iommu_init or from the interrupt + * remapping setup code. * * This function basically parses the ACPI table for AMD IOMMU (IVRS) * three times: @@ -1446,16 +1476,21 @@ static struct syscore_ops amd_iommu_syscore_ops = { * remapping requirements parsed out of the ACPI table in * this last pass. * - * After that the hardware is initialized and ready to go. In the last - * step we do some Linux specific things like registering the driver in - * the dma_ops interface and initializing the suspend/resume support - * functions. Finally it prints some information about AMD IOMMUs and - * the driver state and enables the hardware. + * After everything is set up the IOMMUs are enabled and the necessary + * hotplug and suspend notifiers are registered. */ -static int __init amd_iommu_init(void) +int __init amd_iommu_init_hardware(void) { int i, ret = 0; + if (!amd_iommu_detected) + return -ENODEV; + + if (amd_iommu_dev_table != NULL) { + /* Hardware already initialized */ + return 0; + } + /* * First parse ACPI tables to find the largest Bus/Dev/Func * we need to handle. Upon this information the shared data @@ -1472,9 +1507,8 @@ static int __init amd_iommu_init(void) alias_table_size = tbl_size(ALIAS_TABLE_ENTRY_SIZE); rlookup_table_size = tbl_size(RLOOKUP_TABLE_ENTRY_SIZE); - ret = -ENOMEM; - /* Device table - directly used by all IOMMUs */ + ret = -ENOMEM; amd_iommu_dev_table = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, get_order(dev_table_size)); if (amd_iommu_dev_table == NULL) @@ -1546,20 +1580,46 @@ static int __init amd_iommu_init(void) enable_iommus(); + amd_iommu_init_notifier(); + + register_syscore_ops(&amd_iommu_syscore_ops); + +out: + return ret; + +free: + free_on_init_error(); + + return ret; +} + +/* + * This is the core init function for AMD IOMMU hardware in the system. + * This function is called from the generic x86 DMA layer initialization + * code. + * + * The function calls amd_iommu_init_hardware() to setup and enable the + * IOMMU hardware if this has not happened yet. After that the driver + * registers for the DMA-API and for the IOMMU-API as necessary. + */ +static int __init amd_iommu_init(void) +{ + int ret = 0; + + ret = amd_iommu_init_hardware(); + if (ret) + goto out; + if (iommu_pass_through) ret = amd_iommu_init_passthrough(); else ret = amd_iommu_init_dma_ops(); if (ret) - goto free_disable; + goto free; amd_iommu_init_api(); - amd_iommu_init_notifier(); - - register_syscore_ops(&amd_iommu_syscore_ops); - if (iommu_pass_through) goto out; @@ -1569,39 +1629,14 @@ static int __init amd_iommu_init(void) printk(KERN_INFO "AMD-Vi: Lazy IO/TLB flushing enabled\n"); x86_platform.iommu_shutdown = disable_iommus; + out: return ret; -free_disable: - disable_iommus(); - free: - amd_iommu_uninit_devices(); - - free_pages((unsigned long)amd_iommu_pd_alloc_bitmap, - get_order(MAX_DOMAIN_ID/8)); - - free_pages((unsigned long)amd_iommu_rlookup_table, - get_order(rlookup_table_size)); - - free_pages((unsigned long)amd_iommu_alias_table, - get_order(alias_table_size)); - - free_pages((unsigned long)amd_iommu_dev_table, - get_order(dev_table_size)); - - free_iommu_all(); - - free_unity_maps(); - -#ifdef CONFIG_GART_IOMMU - /* - * We failed to initialize the AMD IOMMU - try fallback to GART - * if possible. - */ - gart_iommu_init(); + disable_iommus(); -#endif + free_on_init_error(); goto out; } diff --git a/include/linux/amd-iommu.h b/include/linux/amd-iommu.h index ef00610837d4..15f6b9edd0b1 100644 --- a/include/linux/amd-iommu.h +++ b/include/linux/amd-iommu.h @@ -28,7 +28,7 @@ struct task_struct; struct pci_dev; extern int amd_iommu_detect(void); - +extern int amd_iommu_init_hardware(void); /** * amd_iommu_enable_device_erratum() - Enable erratum workaround for device -- cgit v1.2.3 From 59e6b9c11341e3b8ac5925427c903d4eae435bd8 Mon Sep 17 00:00:00 2001 From: Bryan Schumaker Date: Fri, 24 Feb 2012 14:14:50 -0500 Subject: Created a function for setting timeouts on keys The keyctl_set_timeout function isn't exported to other parts of the kernel, but I want to use it for the NFS idmapper. I already have the key, but I wanted a generic way to set the timeout. Signed-off-by: Bryan Schumaker Acked-by: David Howells Signed-off-by: Trond Myklebust --- include/linux/key.h | 2 ++ security/keys/key.c | 20 ++++++++++++++++++++ security/keys/keyctl.c | 18 ++---------------- 3 files changed, 24 insertions(+), 16 deletions(-) (limited to 'include/linux') diff --git a/include/linux/key.h b/include/linux/key.h index 5253471cd2ea..be3995d1024a 100644 --- a/include/linux/key.h +++ b/include/linux/key.h @@ -276,6 +276,8 @@ static inline key_serial_t key_serial(const struct key *key) return key ? key->serial : 0; } +extern void key_set_timeout(struct key *, unsigned); + /** * key_is_instantiated - Determine if a key has been positively instantiated * @key: The key to check. diff --git a/security/keys/key.c b/security/keys/key.c index 7ada8019be1f..06783cffb3af 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -671,6 +671,26 @@ found_kernel_type: return ktype; } +void key_set_timeout(struct key *key, unsigned timeout) +{ + struct timespec now; + time_t expiry = 0; + + /* make the changes with the locks held to prevent races */ + down_write(&key->sem); + + if (timeout > 0) { + now = current_kernel_time(); + expiry = now.tv_sec + timeout; + } + + key->expiry = expiry; + key_schedule_gc(key->expiry + key_gc_delay); + + up_write(&key->sem); +} +EXPORT_SYMBOL_GPL(key_set_timeout); + /* * Unlock a key type locked by key_type_lookup(). */ diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index 0b3f5d72af1c..0a4a21d73f6a 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -1244,10 +1245,8 @@ error: */ long keyctl_set_timeout(key_serial_t id, unsigned timeout) { - struct timespec now; struct key *key, *instkey; key_ref_t key_ref; - time_t expiry; long ret; key_ref = lookup_user_key(id, KEY_LOOKUP_CREATE | KEY_LOOKUP_PARTIAL, @@ -1273,20 +1272,7 @@ long keyctl_set_timeout(key_serial_t id, unsigned timeout) okay: key = key_ref_to_ptr(key_ref); - - /* make the changes with the locks held to prevent races */ - down_write(&key->sem); - - expiry = 0; - if (timeout > 0) { - now = current_kernel_time(); - expiry = now.tv_sec + timeout; - } - - key->expiry = expiry; - key_schedule_gc(key->expiry + key_gc_delay); - - up_write(&key->sem); + key_set_timeout(key, timeout); key_put(key); ret = 0; -- cgit v1.2.3 From 57e62324e469e092ecc6c94a7a86fe4bd6ac5172 Mon Sep 17 00:00:00 2001 From: Bryan Schumaker Date: Fri, 24 Feb 2012 14:14:51 -0500 Subject: NFS: Store the legacy idmapper result in the keyring This patch removes the old hashmap-based caching and instead uses a "request key actor" to place an upcall to the legacy idmapper rather than going through /sbin/request-key. This will only be used as a fallback if /etc/request-key.conf isn't configured to use nfsidmap. Signed-off-by: Bryan Schumaker Signed-off-by: Trond Myklebust --- fs/nfs/idmap.c | 554 ++++++++++++++-------------------------------- include/linux/nfs_idmap.h | 11 - 2 files changed, 166 insertions(+), 399 deletions(-) (limited to 'include/linux') diff --git a/fs/nfs/idmap.c b/fs/nfs/idmap.c index d4db3b6f4b8e..f72c1fc074e1 100644 --- a/fs/nfs/idmap.c +++ b/fs/nfs/idmap.c @@ -34,42 +34,28 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include -#include -#include -#include +#include +#include #include +#include +#include #include -#include -#include -#include #include +#include #include #include -#include -#include #include - -/* include files needed by legacy idmapper */ #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "nfs4_fs.h" + #include "internal.h" #include "netns.h" #define NFS_UINT_MAXLEN 11 -#define IDMAP_HASH_SZ 128 /* Default cache timeout is 10 minutes */ -unsigned int nfs_idmap_cache_timeout = 600 * HZ; +unsigned int nfs_idmap_cache_timeout = 600; const struct cred *id_resolver_cache; +struct key_type key_type_id_resolver_legacy; /** @@ -261,8 +247,10 @@ static ssize_t nfs_idmap_get_desc(const char *name, size_t namelen, return desclen; } -static ssize_t nfs_idmap_request_key(const char *name, size_t namelen, - const char *type, void *data, size_t data_size) +static ssize_t nfs_idmap_request_key(struct key_type *key_type, + const char *name, size_t namelen, + const char *type, void *data, + size_t data_size, struct idmap *idmap) { const struct cred *saved_cred; struct key *rkey; @@ -275,8 +263,12 @@ static ssize_t nfs_idmap_request_key(const char *name, size_t namelen, goto out; saved_cred = override_creds(id_resolver_cache); - rkey = request_key(&key_type_id_resolver, desc, ""); + if (idmap) + rkey = request_key_with_auxdata(key_type, desc, "", 0, idmap); + else + rkey = request_key(&key_type_id_resolver, desc, ""); revert_creds(saved_cred); + kfree(desc); if (IS_ERR(rkey)) { ret = PTR_ERR(rkey); @@ -309,31 +301,46 @@ out: return ret; } +static ssize_t nfs_idmap_get_key(const char *name, size_t namelen, + const char *type, void *data, + size_t data_size, struct idmap *idmap) +{ + ssize_t ret = nfs_idmap_request_key(&key_type_id_resolver, + name, namelen, type, data, + data_size, NULL); + if (ret < 0) { + ret = nfs_idmap_request_key(&key_type_id_resolver_legacy, + name, namelen, type, data, + data_size, idmap); + } + return ret; +} /* ID -> Name */ -static ssize_t nfs_idmap_lookup_name(__u32 id, const char *type, char *buf, size_t buflen) +static ssize_t nfs_idmap_lookup_name(__u32 id, const char *type, char *buf, + size_t buflen, struct idmap *idmap) { char id_str[NFS_UINT_MAXLEN]; int id_len; ssize_t ret; id_len = snprintf(id_str, sizeof(id_str), "%u", id); - ret = nfs_idmap_request_key(id_str, id_len, type, buf, buflen); + ret = nfs_idmap_get_key(id_str, id_len, type, buf, buflen, idmap); if (ret < 0) return -EINVAL; return ret; } /* Name -> ID */ -static int nfs_idmap_lookup_id(const char *name, size_t namelen, - const char *type, __u32 *id) +static int nfs_idmap_lookup_id(const char *name, size_t namelen, const char *type, + __u32 *id, struct idmap *idmap) { char id_str[NFS_UINT_MAXLEN]; long id_long; ssize_t data_size; int ret = 0; - data_size = nfs_idmap_request_key(name, namelen, type, id_str, NFS_UINT_MAXLEN); + data_size = nfs_idmap_get_key(name, namelen, type, id_str, NFS_UINT_MAXLEN, idmap); if (data_size <= 0) { ret = -EINVAL; } else { @@ -344,54 +351,47 @@ static int nfs_idmap_lookup_id(const char *name, size_t namelen, } /* idmap classic begins here */ -static int param_set_idmap_timeout(const char *val, struct kernel_param *kp) -{ - char *endp; - int num = simple_strtol(val, &endp, 0); - int jif = num * HZ; - if (endp == val || *endp || num < 0 || jif < num) - return -EINVAL; - *((int *)kp->arg) = jif; - return 0; -} - -module_param_call(idmap_cache_timeout, param_set_idmap_timeout, param_get_int, - &nfs_idmap_cache_timeout, 0644); +module_param(nfs_idmap_cache_timeout, int, 0644); -struct idmap_hashent { - unsigned long ih_expires; - __u32 ih_id; - size_t ih_namelen; - const char *ih_name; +struct idmap { + struct rpc_pipe *idmap_pipe; + struct key_construction *idmap_key_cons; }; -struct idmap_hashtable { - __u8 h_type; - struct idmap_hashent *h_entries; +enum { + Opt_find_uid, Opt_find_gid, Opt_find_user, Opt_find_group, Opt_find_err }; -struct idmap { - struct rpc_pipe *idmap_pipe; - wait_queue_head_t idmap_wq; - struct idmap_msg idmap_im; - struct mutex idmap_lock; /* Serializes upcalls */ - struct mutex idmap_im_lock; /* Protects the hashtable */ - struct idmap_hashtable idmap_user_hash; - struct idmap_hashtable idmap_group_hash; +static const match_table_t nfs_idmap_tokens = { + { Opt_find_uid, "uid:%s" }, + { Opt_find_gid, "gid:%s" }, + { Opt_find_user, "user:%s" }, + { Opt_find_group, "group:%s" }, + { Opt_find_err, NULL } }; +static int nfs_idmap_legacy_upcall(struct key_construction *, const char *, void *); static ssize_t idmap_pipe_downcall(struct file *, const char __user *, size_t); static void idmap_pipe_destroy_msg(struct rpc_pipe_msg *); -static unsigned int fnvhash32(const void *, size_t); - static const struct rpc_pipe_ops idmap_upcall_ops = { .upcall = rpc_pipe_generic_upcall, .downcall = idmap_pipe_downcall, .destroy_msg = idmap_pipe_destroy_msg, }; +struct key_type key_type_id_resolver_legacy = { + .name = "id_resolver", + .instantiate = user_instantiate, + .match = user_match, + .revoke = user_revoke, + .destroy = user_destroy, + .describe = user_describe, + .read = user_read, + .request_key = nfs_idmap_legacy_upcall, +}; + static void __nfs_idmap_unregister(struct rpc_pipe *pipe) { if (pipe->dentry) @@ -468,38 +468,11 @@ nfs_idmap_new(struct nfs_client *clp) return error; } idmap->idmap_pipe = pipe; - mutex_init(&idmap->idmap_lock); - mutex_init(&idmap->idmap_im_lock); - init_waitqueue_head(&idmap->idmap_wq); - idmap->idmap_user_hash.h_type = IDMAP_TYPE_USER; - idmap->idmap_group_hash.h_type = IDMAP_TYPE_GROUP; clp->cl_idmap = idmap; return 0; } -static void -idmap_alloc_hashtable(struct idmap_hashtable *h) -{ - if (h->h_entries != NULL) - return; - h->h_entries = kcalloc(IDMAP_HASH_SZ, - sizeof(*h->h_entries), - GFP_KERNEL); -} - -static void -idmap_free_hashtable(struct idmap_hashtable *h) -{ - int i; - - if (h->h_entries == NULL) - return; - for (i = 0; i < IDMAP_HASH_SZ; i++) - kfree(h->h_entries[i].ih_name); - kfree(h->h_entries); -} - void nfs_idmap_delete(struct nfs_client *clp) { @@ -510,8 +483,6 @@ nfs_idmap_delete(struct nfs_client *clp) nfs_idmap_unregister(clp, idmap->idmap_pipe); rpc_destroy_pipe_data(idmap->idmap_pipe); clp->cl_idmap = NULL; - idmap_free_hashtable(&idmap->idmap_user_hash); - idmap_free_hashtable(&idmap->idmap_group_hash); kfree(idmap); } @@ -617,222 +588,107 @@ void nfs_idmap_quit(void) nfs_idmap_quit_keyring(); } -/* - * Helper routines for manipulating the hashtable - */ -static inline struct idmap_hashent * -idmap_name_hash(struct idmap_hashtable* h, const char *name, size_t len) -{ - if (h->h_entries == NULL) - return NULL; - return &h->h_entries[fnvhash32(name, len) % IDMAP_HASH_SZ]; -} - -static struct idmap_hashent * -idmap_lookup_name(struct idmap_hashtable *h, const char *name, size_t len) +static int nfs_idmap_prepare_message(char *desc, struct idmap_msg *im, + struct rpc_pipe_msg *msg) { - struct idmap_hashent *he = idmap_name_hash(h, name, len); + substring_t substr; + int token, ret; - if (he == NULL) - return NULL; - if (he->ih_namelen != len || memcmp(he->ih_name, name, len) != 0) - return NULL; - if (time_after(jiffies, he->ih_expires)) - return NULL; - return he; -} + memset(im, 0, sizeof(*im)); + memset(msg, 0, sizeof(*msg)); -static inline struct idmap_hashent * -idmap_id_hash(struct idmap_hashtable* h, __u32 id) -{ - if (h->h_entries == NULL) - return NULL; - return &h->h_entries[fnvhash32(&id, sizeof(id)) % IDMAP_HASH_SZ]; -} + im->im_type = IDMAP_TYPE_GROUP; + token = match_token(desc, nfs_idmap_tokens, &substr); -static struct idmap_hashent * -idmap_lookup_id(struct idmap_hashtable *h, __u32 id) -{ - struct idmap_hashent *he = idmap_id_hash(h, id); + switch (token) { + case Opt_find_uid: + im->im_type = IDMAP_TYPE_USER; + case Opt_find_gid: + im->im_conv = IDMAP_CONV_NAMETOID; + ret = match_strlcpy(im->im_name, &substr, IDMAP_NAMESZ); + break; - if (he == NULL) - return NULL; - if (he->ih_id != id || he->ih_namelen == 0) - return NULL; - if (time_after(jiffies, he->ih_expires)) - return NULL; - return he; -} + case Opt_find_user: + im->im_type = IDMAP_TYPE_USER; + case Opt_find_group: + im->im_conv = IDMAP_CONV_IDTONAME; + ret = match_int(&substr, &im->im_id); + break; -/* - * Routines for allocating new entries in the hashtable. - * For now, we just have 1 entry per bucket, so it's all - * pretty trivial. - */ -static inline struct idmap_hashent * -idmap_alloc_name(struct idmap_hashtable *h, char *name, size_t len) -{ - idmap_alloc_hashtable(h); - return idmap_name_hash(h, name, len); -} + default: + ret = -EINVAL; + goto out; + } -static inline struct idmap_hashent * -idmap_alloc_id(struct idmap_hashtable *h, __u32 id) -{ - idmap_alloc_hashtable(h); - return idmap_id_hash(h, id); -} + msg->data = im; + msg->len = sizeof(struct idmap_msg); -static void -idmap_update_entry(struct idmap_hashent *he, const char *name, - size_t namelen, __u32 id) -{ - char *str = kmalloc(namelen + 1, GFP_KERNEL); - if (str == NULL) - return; - kfree(he->ih_name); - he->ih_id = id; - memcpy(str, name, namelen); - str[namelen] = '\0'; - he->ih_name = str; - he->ih_namelen = namelen; - he->ih_expires = jiffies + nfs_idmap_cache_timeout; +out: + return ret; } -/* - * Name -> ID - */ -static int -nfs_idmap_id(struct idmap *idmap, struct idmap_hashtable *h, - const char *name, size_t namelen, __u32 *id) +static int nfs_idmap_legacy_upcall(struct key_construction *cons, + const char *op, + void *aux) { - struct rpc_pipe_msg msg; + struct rpc_pipe_msg *msg; struct idmap_msg *im; - struct idmap_hashent *he; - DECLARE_WAITQUEUE(wq, current); - int ret = -EIO; - - im = &idmap->idmap_im; - - /* - * String sanity checks - * Note that the userland daemon expects NUL terminated strings - */ - for (;;) { - if (namelen == 0) - return -EINVAL; - if (name[namelen-1] != '\0') - break; - namelen--; - } - if (namelen >= IDMAP_NAMESZ) - return -EINVAL; - - mutex_lock(&idmap->idmap_lock); - mutex_lock(&idmap->idmap_im_lock); + struct idmap *idmap = (struct idmap *)aux; + struct key *key = cons->key; + int ret; - he = idmap_lookup_name(h, name, namelen); - if (he != NULL) { - *id = he->ih_id; - ret = 0; - goto out; + /* msg and im are freed in idmap_pipe_destroy_msg */ + msg = kmalloc(sizeof(*msg), GFP_KERNEL); + if (IS_ERR(msg)) { + ret = PTR_ERR(msg); + goto out0; } - memset(im, 0, sizeof(*im)); - memcpy(im->im_name, name, namelen); - - im->im_type = h->h_type; - im->im_conv = IDMAP_CONV_NAMETOID; - - memset(&msg, 0, sizeof(msg)); - msg.data = im; - msg.len = sizeof(*im); - - add_wait_queue(&idmap->idmap_wq, &wq); - if (rpc_queue_upcall(idmap->idmap_pipe, &msg) < 0) { - remove_wait_queue(&idmap->idmap_wq, &wq); - goto out; + im = kmalloc(sizeof(*im), GFP_KERNEL); + if (IS_ERR(im)) { + ret = PTR_ERR(im); + goto out1; } - set_current_state(TASK_UNINTERRUPTIBLE); - mutex_unlock(&idmap->idmap_im_lock); - schedule(); - __set_current_state(TASK_RUNNING); - remove_wait_queue(&idmap->idmap_wq, &wq); - mutex_lock(&idmap->idmap_im_lock); + ret = nfs_idmap_prepare_message(key->description, im, msg); + if (ret < 0) + goto out2; - if (im->im_status & IDMAP_STATUS_SUCCESS) { - *id = im->im_id; - ret = 0; - } + idmap->idmap_key_cons = cons; - out: - memset(im, 0, sizeof(*im)); - mutex_unlock(&idmap->idmap_im_lock); - mutex_unlock(&idmap->idmap_lock); + return rpc_queue_upcall(idmap->idmap_pipe, msg); + +out2: + kfree(im); +out1: + kfree(msg); +out0: + complete_request_key(cons, ret); return ret; } -/* - * ID -> Name - */ -static int -nfs_idmap_name(struct idmap *idmap, struct idmap_hashtable *h, - __u32 id, char *name) +static int nfs_idmap_instantiate(struct key *key, struct key *authkey, char *data) { - struct rpc_pipe_msg msg; - struct idmap_msg *im; - struct idmap_hashent *he; - DECLARE_WAITQUEUE(wq, current); - int ret = -EIO; - unsigned int len; - - im = &idmap->idmap_im; - - mutex_lock(&idmap->idmap_lock); - mutex_lock(&idmap->idmap_im_lock); - - he = idmap_lookup_id(h, id); - if (he) { - memcpy(name, he->ih_name, he->ih_namelen); - ret = he->ih_namelen; - goto out; - } - - memset(im, 0, sizeof(*im)); - im->im_type = h->h_type; - im->im_conv = IDMAP_CONV_IDTONAME; - im->im_id = id; - - memset(&msg, 0, sizeof(msg)); - msg.data = im; - msg.len = sizeof(*im); - - add_wait_queue(&idmap->idmap_wq, &wq); + return key_instantiate_and_link(key, data, strlen(data) + 1, + id_resolver_cache->thread_keyring, + authkey); +} - if (rpc_queue_upcall(idmap->idmap_pipe, &msg) < 0) { - remove_wait_queue(&idmap->idmap_wq, &wq); - goto out; - } +static int nfs_idmap_read_message(struct idmap_msg *im, struct key *key, struct key *authkey) +{ + char id_str[NFS_UINT_MAXLEN]; + int ret = -EINVAL; - set_current_state(TASK_UNINTERRUPTIBLE); - mutex_unlock(&idmap->idmap_im_lock); - schedule(); - __set_current_state(TASK_RUNNING); - remove_wait_queue(&idmap->idmap_wq, &wq); - mutex_lock(&idmap->idmap_im_lock); - - if (im->im_status & IDMAP_STATUS_SUCCESS) { - if ((len = strnlen(im->im_name, IDMAP_NAMESZ)) == 0) - goto out; - memcpy(name, im->im_name, len); - ret = len; + switch (im->im_conv) { + case IDMAP_CONV_NAMETOID: + sprintf(id_str, "%d", im->im_id); + ret = nfs_idmap_instantiate(key, authkey, id_str); + break; + case IDMAP_CONV_IDTONAME: + ret = nfs_idmap_instantiate(key, authkey, im->im_name); + break; } - out: - memset(im, 0, sizeof(*im)); - mutex_unlock(&idmap->idmap_im_lock); - mutex_unlock(&idmap->idmap_lock); return ret; } @@ -841,141 +697,69 @@ idmap_pipe_downcall(struct file *filp, const char __user *src, size_t mlen) { struct rpc_inode *rpci = RPC_I(filp->f_path.dentry->d_inode); struct idmap *idmap = (struct idmap *)rpci->private; - struct idmap_msg im_in, *im = &idmap->idmap_im; - struct idmap_hashtable *h; - struct idmap_hashent *he = NULL; + struct key_construction *cons = idmap->idmap_key_cons; + struct idmap_msg im; size_t namelen_in; int ret; - if (mlen != sizeof(im_in)) - return -ENOSPC; - - if (copy_from_user(&im_in, src, mlen) != 0) - return -EFAULT; - - mutex_lock(&idmap->idmap_im_lock); - - ret = mlen; - im->im_status = im_in.im_status; - /* If we got an error, terminate now, and wake up pending upcalls */ - if (!(im_in.im_status & IDMAP_STATUS_SUCCESS)) { - wake_up(&idmap->idmap_wq); + if (mlen != sizeof(im)) { + ret = -ENOSPC; goto out; } - /* Sanity checking of strings */ - ret = -EINVAL; - namelen_in = strnlen(im_in.im_name, IDMAP_NAMESZ); - if (namelen_in == 0 || namelen_in == IDMAP_NAMESZ) + if (copy_from_user(&im, src, mlen) != 0) { + ret = -EFAULT; goto out; + } - switch (im_in.im_type) { - case IDMAP_TYPE_USER: - h = &idmap->idmap_user_hash; - break; - case IDMAP_TYPE_GROUP: - h = &idmap->idmap_group_hash; - break; - default: - goto out; + if (!(im.im_status & IDMAP_STATUS_SUCCESS)) { + ret = mlen; + complete_request_key(idmap->idmap_key_cons, -ENOKEY); + goto out_incomplete; } - switch (im_in.im_conv) { - case IDMAP_CONV_IDTONAME: - /* Did we match the current upcall? */ - if (im->im_conv == IDMAP_CONV_IDTONAME - && im->im_type == im_in.im_type - && im->im_id == im_in.im_id) { - /* Yes: copy string, including the terminating '\0' */ - memcpy(im->im_name, im_in.im_name, namelen_in); - im->im_name[namelen_in] = '\0'; - wake_up(&idmap->idmap_wq); - } - he = idmap_alloc_id(h, im_in.im_id); - break; - case IDMAP_CONV_NAMETOID: - /* Did we match the current upcall? */ - if (im->im_conv == IDMAP_CONV_NAMETOID - && im->im_type == im_in.im_type - && strnlen(im->im_name, IDMAP_NAMESZ) == namelen_in - && memcmp(im->im_name, im_in.im_name, namelen_in) == 0) { - im->im_id = im_in.im_id; - wake_up(&idmap->idmap_wq); - } - he = idmap_alloc_name(h, im_in.im_name, namelen_in); - break; - default: + namelen_in = strnlen(im.im_name, IDMAP_NAMESZ); + if (namelen_in == 0 || namelen_in == IDMAP_NAMESZ) { + ret = -EINVAL; goto out; } - /* If the entry is valid, also copy it to the cache */ - if (he != NULL) - idmap_update_entry(he, im_in.im_name, namelen_in, im_in.im_id); - ret = mlen; + ret = nfs_idmap_read_message(&im, cons->key, cons->authkey); + if (ret >= 0) { + key_set_timeout(cons->key, nfs_idmap_cache_timeout); + ret = mlen; + } + out: - mutex_unlock(&idmap->idmap_im_lock); + complete_request_key(idmap->idmap_key_cons, ret); +out_incomplete: return ret; } static void idmap_pipe_destroy_msg(struct rpc_pipe_msg *msg) { - struct idmap_msg *im = msg->data; - struct idmap *idmap = container_of(im, struct idmap, idmap_im); - - if (msg->errno >= 0) - return; - mutex_lock(&idmap->idmap_im_lock); - im->im_status = IDMAP_STATUS_LOOKUPFAIL; - wake_up(&idmap->idmap_wq); - mutex_unlock(&idmap->idmap_im_lock); -} - -/* - * Fowler/Noll/Vo hash - * http://www.isthe.com/chongo/tech/comp/fnv/ - */ - -#define FNV_P_32 ((unsigned int)0x01000193) /* 16777619 */ -#define FNV_1_32 ((unsigned int)0x811c9dc5) /* 2166136261 */ - -static unsigned int fnvhash32(const void *buf, size_t buflen) -{ - const unsigned char *p, *end = (const unsigned char *)buf + buflen; - unsigned int hash = FNV_1_32; - - for (p = buf; p < end; p++) { - hash *= FNV_P_32; - hash ^= (unsigned int)*p; - } - - return hash; + /* Free memory allocated in nfs_idmap_legacy_upcall() */ + kfree(msg->data); + kfree(msg); } int nfs_map_name_to_uid(const struct nfs_server *server, const char *name, size_t namelen, __u32 *uid) { struct idmap *idmap = server->nfs_client->cl_idmap; - int ret = -EINVAL; if (nfs_map_string_to_numeric(name, namelen, uid)) return 0; - ret = nfs_idmap_lookup_id(name, namelen, "uid", uid); - if (ret < 0) - ret = nfs_idmap_id(idmap, &idmap->idmap_user_hash, name, namelen, uid); - return ret; + return nfs_idmap_lookup_id(name, namelen, "uid", uid, idmap); } int nfs_map_group_to_gid(const struct nfs_server *server, const char *name, size_t namelen, __u32 *gid) { struct idmap *idmap = server->nfs_client->cl_idmap; - int ret = -EINVAL; if (nfs_map_string_to_numeric(name, namelen, gid)) return 0; - ret = nfs_idmap_lookup_id(name, namelen, "gid", gid); - if (ret < 0) - ret = nfs_idmap_id(idmap, &idmap->idmap_group_hash, name, namelen, gid); - return ret; + return nfs_idmap_lookup_id(name, namelen, "gid", gid, idmap); } int nfs_map_uid_to_name(const struct nfs_server *server, __u32 uid, char *buf, size_t buflen) @@ -983,11 +767,8 @@ int nfs_map_uid_to_name(const struct nfs_server *server, __u32 uid, char *buf, s struct idmap *idmap = server->nfs_client->cl_idmap; int ret = -EINVAL; - if (!(server->caps & NFS_CAP_UIDGID_NOMAP)) { - ret = nfs_idmap_lookup_name(uid, "user", buf, buflen); - if (ret < 0) - ret = nfs_idmap_name(idmap, &idmap->idmap_user_hash, uid, buf); - } + if (!(server->caps & NFS_CAP_UIDGID_NOMAP)) + ret = nfs_idmap_lookup_name(uid, "user", buf, buflen, idmap); if (ret < 0) ret = nfs_map_numeric_to_string(uid, buf, buflen); return ret; @@ -997,11 +778,8 @@ int nfs_map_gid_to_group(const struct nfs_server *server, __u32 gid, char *buf, struct idmap *idmap = server->nfs_client->cl_idmap; int ret = -EINVAL; - if (!(server->caps & NFS_CAP_UIDGID_NOMAP)) { - ret = nfs_idmap_lookup_name(gid, "group", buf, buflen); - if (ret < 0) - ret = nfs_idmap_name(idmap, &idmap->idmap_group_hash, gid, buf); - } + if (!(server->caps & NFS_CAP_UIDGID_NOMAP)) + ret = nfs_idmap_lookup_name(gid, "group", buf, buflen, idmap); if (ret < 0) ret = nfs_map_numeric_to_string(gid, buf, buflen); return ret; diff --git a/include/linux/nfs_idmap.h b/include/linux/nfs_idmap.h index 7eed2012d288..717fa5019e75 100644 --- a/include/linux/nfs_idmap.h +++ b/include/linux/nfs_idmap.h @@ -69,19 +69,8 @@ struct nfs_server; struct nfs_fattr; struct nfs4_string; -#ifdef CONFIG_NFS_V4 int nfs_idmap_init(void); void nfs_idmap_quit(void); -#else -static inline int nfs_idmap_init(void) -{ - return 0; -} - -static inline void nfs_idmap_quit(void) -{} -#endif - int nfs_idmap_new(struct nfs_client *); void nfs_idmap_delete(struct nfs_client *); -- cgit v1.2.3 From 7d2ed9ac22bc6bf0d34e8fd291a5295f373b384e Mon Sep 17 00:00:00 2001 From: Weston Andros Adamson Date: Fri, 17 Feb 2012 15:20:26 -0500 Subject: NFSv4: parse and display server implementation ids Shows the implementation ids in /proc/self/mountstats. This doesn't break the nfs-utils mountstats tool. Signed-off-by: Weston Andros Adamson Signed-off-by: Trond Myklebust --- fs/nfs/client.c | 1 + fs/nfs/nfs4proc.c | 21 +++++++++++++++++++++ fs/nfs/nfs4xdr.c | 42 +++++++++++++++++++++++++++++++++++++----- fs/nfs/super.c | 8 ++++++++ include/linux/nfs_fs_sb.h | 2 ++ include/linux/nfs_xdr.h | 15 +++++++-------- 6 files changed, 76 insertions(+), 13 deletions(-) (limited to 'include/linux') diff --git a/fs/nfs/client.c b/fs/nfs/client.c index 592b5583aa3a..1506adf4d4ed 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -304,6 +304,7 @@ static void nfs_free_client(struct nfs_client *clp) put_net(clp->net); kfree(clp->cl_hostname); kfree(clp->server_scope); + kfree(clp->impl_id); kfree(clp); dprintk("<-- nfs_free_client()\n"); diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 20c3bb06763a..90a17cc3ebc9 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -4950,10 +4950,23 @@ int nfs4_proc_exchange_id(struct nfs_client *clp, struct rpc_cred *cred) goto out; } + res.impl_id = kzalloc(sizeof(struct nfs41_impl_id), GFP_KERNEL); + if (unlikely(!res.impl_id)) { + status = -ENOMEM; + goto out_server_scope; + } + status = rpc_call_sync(clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT); if (!status) status = nfs4_check_cl_exchange_flags(clp->cl_exchange_flags); + if (!status) { + /* use the most recent implementation id */ + kfree(clp->impl_id); + clp->impl_id = res.impl_id; + } else + kfree(res.impl_id); + if (!status) { if (clp->server_scope && !nfs41_same_server_scope(clp->server_scope, @@ -4970,8 +4983,16 @@ int nfs4_proc_exchange_id(struct nfs_client *clp, struct rpc_cred *cred) goto out; } } + +out_server_scope: kfree(res.server_scope); out: + if (clp->impl_id) + dprintk("%s: Server Implementation ID: " + "domain: %s, name: %s, date: %llu,%u\n", + __func__, clp->impl_id->domain, clp->impl_id->name, + clp->impl_id->date.seconds, + clp->impl_id->date.nseconds); dprintk("<-- %s status= %d\n", __func__, status); return status; } diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index d824aedb1237..b7c04339fdc1 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -291,7 +291,11 @@ static int nfs4_stat_to_errno(int); /* eir_server_scope<> */ \ XDR_QUADLEN(NFS4_OPAQUE_LIMIT) + 1 + \ 1 /* eir_server_impl_id array length */ + \ - 0 /* ignored eir_server_impl_id contents */) + 1 /* nii_domain */ + \ + XDR_QUADLEN(NFS4_OPAQUE_LIMIT) + \ + 1 /* nii_name */ + \ + XDR_QUADLEN(NFS4_OPAQUE_LIMIT) + \ + 3 /* nii_date */) #define encode_channel_attrs_maxsz (6 + 1 /* ca_rdma_ird.len (0) */) #define decode_channel_attrs_maxsz (6 + \ 1 /* ca_rdma_ird.len */ + \ @@ -5256,6 +5260,7 @@ static int decode_exchange_id(struct xdr_stream *xdr, char *dummy_str; int status; struct nfs_client *clp = res->client; + uint32_t impl_id_count; status = decode_op_hdr(xdr, OP_EXCHANGE_ID); if (status) @@ -5297,11 +5302,38 @@ static int decode_exchange_id(struct xdr_stream *xdr, memcpy(res->server_scope->server_scope, dummy_str, dummy); res->server_scope->server_scope_sz = dummy; - /* Throw away Implementation id array */ - status = decode_opaque_inline(xdr, &dummy, &dummy_str); - if (unlikely(status)) - return status; + /* Implementation Id */ + p = xdr_inline_decode(xdr, 4); + if (unlikely(!p)) + goto out_overflow; + impl_id_count = be32_to_cpup(p++); + if (impl_id_count) { + /* nii_domain */ + status = decode_opaque_inline(xdr, &dummy, &dummy_str); + if (unlikely(status)) + return status; + if (unlikely(dummy > NFS4_OPAQUE_LIMIT)) + return -EIO; + memcpy(res->impl_id->domain, dummy_str, dummy); + + /* nii_name */ + status = decode_opaque_inline(xdr, &dummy, &dummy_str); + if (unlikely(status)) + return status; + if (unlikely(dummy > NFS4_OPAQUE_LIMIT)) + return -EIO; + memcpy(res->impl_id->name, dummy_str, dummy); + + /* nii_date */ + p = xdr_inline_decode(xdr, 12); + if (unlikely(!p)) + goto out_overflow; + p = xdr_decode_hyper(p, &res->impl_id->date.seconds); + res->impl_id->date.nseconds = be32_to_cpup(p); + + /* if there's more than one entry, ignore the rest */ + } return 0; out_overflow: print_overflow_msg(__func__, xdr); diff --git a/fs/nfs/super.c b/fs/nfs/super.c index 6708f3044eb0..8154accd1168 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -809,6 +809,14 @@ static int nfs_show_stats(struct seq_file *m, struct dentry *root) seq_printf(m, "\n\tage:\t%lu", (jiffies - nfss->mount_time) / HZ); + if (nfss->nfs_client && nfss->nfs_client->impl_id) { + struct nfs41_impl_id *impl_id = nfss->nfs_client->impl_id; + seq_printf(m, "\n\timpl_id:\tname='%s',domain='%s'," + "date='%llu,%u'", + impl_id->name, impl_id->domain, + impl_id->date.seconds, impl_id->date.nseconds); + } + seq_printf(m, "\n\tcaps:\t"); seq_printf(m, "caps=0x%x", nfss->caps); seq_printf(m, ",wtmult=%u", nfss->wtmult); diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h index 3bf47666646e..03d0b91c2d5b 100644 --- a/include/linux/nfs_fs_sb.h +++ b/include/linux/nfs_fs_sb.h @@ -18,6 +18,7 @@ struct nfs4_sequence_res; struct nfs_server; struct nfs4_minor_version_ops; struct server_scope; +struct nfs41_impl_id; /* * The nfs_client identifies our client state to the server. @@ -86,6 +87,7 @@ struct nfs_client { #endif struct server_scope *server_scope; /* from exchange_id */ + struct nfs41_impl_id *impl_id; /* from exchange_id */ struct net *net; }; diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index adbc84ac345f..046c1bfddc33 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -1054,14 +1054,6 @@ struct nfstime4 { }; #ifdef CONFIG_NFS_V4_1 -struct nfs_impl_id4 { - u32 domain_len; - char *domain; - u32 name_len; - char *name; - struct nfstime4 date; -}; - #define NFS4_EXCHANGE_ID_LEN (48) struct nfs41_exchange_id_args { struct nfs_client *client; @@ -1082,10 +1074,17 @@ struct server_scope { char server_scope[NFS4_OPAQUE_LIMIT]; }; +struct nfs41_impl_id { + char domain[NFS4_OPAQUE_LIMIT + 1]; + char name[NFS4_OPAQUE_LIMIT + 1]; + struct nfstime4 date; +}; + struct nfs41_exchange_id_res { struct nfs_client *client; u32 flags; struct server_scope *server_scope; + struct nfs41_impl_id *impl_id; }; struct nfs41_create_session_args { -- cgit v1.2.3 From 46919ae63d4820e76724beb655274ce143f0da0b Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Thu, 1 Mar 2012 18:48:32 -0700 Subject: pinctrl: introduce PINCTRL_STATE_DEFAULT, define hogs as that state This provides a single centralized name for the default state. Update PIN_MAP_* macros to use this state name, instead of requiring the user to pass a state name in. With this change, hog entries in the mapping table are defined as those with state name PINCTRL_STATE_DEFAULT, i.e. all entries have the same name. This interacts badly with the nested iteration over mapping table entries in pinctrl_hog_maps() and pinctrl_hog_map() which would now attempt to claim each hog mapping table entry multiple times. Replacing the custom hog code with a simple pinctrl_get()/pinctrl_enable(). Update documentation and mapping tables to use this. Signed-off-by: Stephen Warren Acked-by: Dong Aisheng Signed-off-by: Linus Walleij --- Documentation/pinctrl.txt | 8 +-- arch/arm/mach-u300/core.c | 6 +- drivers/pinctrl/core.c | 145 ++-------------------------------------- drivers/pinctrl/core.h | 4 +- include/linux/pinctrl/machine.h | 13 ++-- include/linux/pinctrl/pinctrl.h | 2 + 6 files changed, 26 insertions(+), 152 deletions(-) (limited to 'include/linux') diff --git a/Documentation/pinctrl.txt b/Documentation/pinctrl.txt index 5e314cecab77..6fe3232e798e 100644 --- a/Documentation/pinctrl.txt +++ b/Documentation/pinctrl.txt @@ -814,7 +814,7 @@ it even more compact which assumes you want to use pinctrl-foo and position 0 for mapping, for example: static struct pinctrl_map __initdata mapping[] = { - PIN_MAP("I2CMAP", "pinctrl-foo", "i2c0", "foo-i2c.0"), + PIN_MAP(PINCTRL_STATE_DEFAULT, "pinctrl-foo", "i2c0", "foo-i2c.0"), }; @@ -930,7 +930,7 @@ foo_probe() /* Allocate a state holder named "state" etc */ struct pinctrl p; - p = pinctrl_get(&device, NULL); + p = pinctrl_get(&device, PINCTRL_STATE_DEFAULT); if IS_ERR(p) return PTR_ERR(p); pinctrl_enable(p); @@ -989,7 +989,7 @@ of the pin controller itself, like this: { .dev_name = "pinctrl-foo", - .name = "POWERMAP" + .name = PINCTRL_STATE_DEFAULT, .ctrl_dev_name = "pinctrl-foo", .function = "power_func", }, @@ -998,7 +998,7 @@ Since it may be common to request the core to hog a few always-applicable mux settings on the primary pin controller, there is a convenience macro for this: -PIN_MAP_PRIMARY_SYS_HOG("POWERMAP", "pinctrl-foo", "power_func") +PIN_MAP_SYS_HOG("pinctrl-foo", "power_func") This gives the exact same result as the above construction. diff --git a/arch/arm/mach-u300/core.c b/arch/arm/mach-u300/core.c index 030b2c0d322d..ea6c79076a91 100644 --- a/arch/arm/mach-u300/core.c +++ b/arch/arm/mach-u300/core.c @@ -1608,9 +1608,9 @@ static struct platform_device dma_device = { /* Pinmux settings */ static struct pinctrl_map __initdata u300_pinmux_map[] = { /* anonymous maps for chip power and EMIFs */ - PIN_MAP_SYS_HOG("POWER", "pinctrl-u300", "power"), - PIN_MAP_SYS_HOG("EMIF0", "pinctrl-u300", "emif0"), - PIN_MAP_SYS_HOG("EMIF1", "pinctrl-u300", "emif1"), + PIN_MAP_SYS_HOG("pinctrl-u300", "power"), + PIN_MAP_SYS_HOG("pinctrl-u300", "emif0"), + PIN_MAP_SYS_HOG("pinctrl-u300", "emif1"), /* per-device maps for MMC/SD, SPI and UART */ PIN_MAP("MMCSD", "pinctrl-u300", "mmc0", "mmci"), PIN_MAP("SPI", "pinctrl-u300", "spi0", "pl022"), diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c index 376cede29662..f25307b0e00a 100644 --- a/drivers/pinctrl/core.c +++ b/drivers/pinctrl/core.c @@ -44,18 +44,6 @@ struct pinctrl_maps { unsigned num_maps; }; -/** - * struct pinctrl_hog - a list item to stash control hogs - * @node: pin control hog list node - * @map: map entry responsible for this hogging - * @pmx: the pin control hogged by this item - */ -struct pinctrl_hog { - struct list_head node; - struct pinctrl_map const *map; - struct pinctrl *p; -}; - /* Global list of pin control devices */ static DEFINE_MUTEX(pinctrldev_list_mutex); static LIST_HEAD(pinctrldev_list); @@ -702,103 +690,6 @@ int pinctrl_register_mappings(struct pinctrl_map const *maps, return 0; } -/* Hog a single map entry and add to the hoglist */ -static int pinctrl_hog_map(struct pinctrl_dev *pctldev, - struct pinctrl_map const *map) -{ - struct pinctrl_hog *hog; - struct pinctrl *p; - int ret; - - hog = kzalloc(sizeof(*hog), GFP_KERNEL); - if (!hog) { - dev_err(pctldev->dev, "failed to alloc struct pinctrl_hog\n"); - return -ENOMEM; - } - - p = pinctrl_get_locked(pctldev->dev, map->name); - if (IS_ERR(p)) { - kfree(hog); - dev_err(pctldev->dev, - "could not get the %s pin control mapping for hogging\n", - map->name); - return PTR_ERR(p); - } - - ret = pinctrl_enable(p); - if (ret) { - pinctrl_put(p); - kfree(hog); - dev_err(pctldev->dev, - "could not enable the %s pin control mapping for hogging\n", - map->name); - return ret; - } - - hog->map = map; - hog->p = p; - - dev_info(pctldev->dev, "hogged map %s, function %s\n", map->name, - map->function); - list_add_tail(&hog->node, &pctldev->pinctrl_hogs); - - return 0; -} - -/** - * pinctrl_hog_maps() - hog specific map entries on controller device - * @pctldev: the pin control device to hog entries on - * - * When the pin controllers are registered, there may be some specific pinmux - * map entries that need to be hogged, i.e. get+enabled until the system shuts - * down. - */ -static int pinctrl_hog_maps(struct pinctrl_dev *pctldev) -{ - struct device *dev = pctldev->dev; - const char *devname = dev_name(dev); - int ret; - struct pinctrl_maps *maps_node; - int i; - struct pinctrl_map const *map; - - INIT_LIST_HEAD(&pctldev->pinctrl_hogs); - - mutex_lock(&pinctrl_maps_mutex); - for_each_maps(maps_node, i, map) { - if (!strcmp(map->ctrl_dev_name, devname) && - !strcmp(map->dev_name, devname)) { - /* OK time to hog! */ - ret = pinctrl_hog_map(pctldev, map); - if (ret) { - mutex_unlock(&pinctrl_maps_mutex); - return ret; - } - } - } - mutex_unlock(&pinctrl_maps_mutex); - - return 0; -} - -/** - * pinctrl_unhog_maps() - unhog specific map entries on controller device - * @pctldev: the pin control device to unhog entries on - */ -static void pinctrl_unhog_maps(struct pinctrl_dev *pctldev) -{ - struct list_head *node, *tmp; - - list_for_each_safe(node, tmp, &pctldev->pinctrl_hogs) { - struct pinctrl_hog *hog = - list_entry(node, struct pinctrl_hog, node); - pinctrl_disable(hog->p); - pinctrl_put(hog->p); - list_del(node); - kfree(hog); - } -} - #ifdef CONFIG_DEBUG_FS static int pinctrl_pins_show(struct seq_file *s, void *what) @@ -889,19 +780,6 @@ static int pinctrl_gpioranges_show(struct seq_file *s, void *what) return 0; } -static int pinmux_hogs_show(struct seq_file *s, void *what) -{ - struct pinctrl_dev *pctldev = s->private; - struct pinctrl_hog *hog; - - seq_puts(s, "Pin control map hogs held by device\n"); - - list_for_each_entry(hog, &pctldev->pinctrl_hogs, node) - seq_printf(s, "%s\n", hog->map->name); - - return 0; -} - static int pinctrl_devices_show(struct seq_file *s, void *what) { struct pinctrl_dev *pctldev; @@ -988,11 +866,6 @@ static int pinctrl_gpioranges_open(struct inode *inode, struct file *file) return single_open(file, pinctrl_gpioranges_show, inode->i_private); } -static int pinmux_hogs_open(struct inode *inode, struct file *file) -{ - return single_open(file, pinmux_hogs_show, inode->i_private); -} - static int pinctrl_devices_open(struct inode *inode, struct file *file) { return single_open(file, pinctrl_devices_show, NULL); @@ -1029,13 +902,6 @@ static const struct file_operations pinctrl_gpioranges_ops = { .release = single_release, }; -static const struct file_operations pinmux_hogs_ops = { - .open = pinmux_hogs_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - static const struct file_operations pinctrl_devices_ops = { .open = pinctrl_devices_open, .read = seq_read, @@ -1078,8 +944,6 @@ static void pinctrl_init_device_debugfs(struct pinctrl_dev *pctldev) device_root, pctldev, &pinctrl_groups_ops); debugfs_create_file("gpio-ranges", S_IFREG | S_IRUGO, device_root, pctldev, &pinctrl_gpioranges_ops); - debugfs_create_file("pinmux-hogs", S_IFREG | S_IRUGO, - device_root, pctldev, &pinmux_hogs_ops); pinmux_init_device_debugfs(device_root, pctldev); pinconf_init_device_debugfs(device_root, pctldev); } @@ -1188,7 +1052,9 @@ struct pinctrl_dev *pinctrl_register(struct pinctrl_desc *pctldesc, mutex_lock(&pinctrldev_list_mutex); list_add_tail(&pctldev->node, &pinctrldev_list); mutex_unlock(&pinctrldev_list_mutex); - pinctrl_hog_maps(pctldev); + pctldev->p = pinctrl_get(pctldev->dev, PINCTRL_STATE_DEFAULT); + if (!IS_ERR(pctldev->p)) + pinctrl_enable(pctldev->p); pinctrl_init_device_debugfs(pctldev); return pctldev; @@ -1211,7 +1077,10 @@ void pinctrl_unregister(struct pinctrl_dev *pctldev) return; pinctrl_remove_device_debugfs(pctldev); - pinctrl_unhog_maps(pctldev); + if (!IS_ERR(pctldev->p)) { + pinctrl_disable(pctldev->p); + pinctrl_put(pctldev->p); + } /* TODO: check that no pinmuxes are still active? */ mutex_lock(&pinctrldev_list_mutex); list_del(&pctldev->node); diff --git a/drivers/pinctrl/core.h b/drivers/pinctrl/core.h index 8164e7b4182b..97f8124b4381 100644 --- a/drivers/pinctrl/core.h +++ b/drivers/pinctrl/core.h @@ -27,7 +27,7 @@ struct pinctrl_gpio_range; * @owner: module providing the pin controller, used for refcounting * @driver_data: driver data for drivers registering to the pin controller * subsystem - * @pinctrl_hogs: list of pin control maps hogged by this device + * @p: result of pinctrl_get() for this device * @device_root: debugfs root for this device */ struct pinctrl_dev { @@ -39,7 +39,7 @@ struct pinctrl_dev { struct device *dev; struct module *owner; void *driver_data; - struct list_head pinctrl_hogs; + struct pinctrl *p; #ifdef CONFIG_DEBUG_FS struct dentry *device_root; #endif diff --git a/include/linux/pinctrl/machine.h b/include/linux/pinctrl/machine.h index 73fbb2745301..20e97353d5f9 100644 --- a/include/linux/pinctrl/machine.h +++ b/include/linux/pinctrl/machine.h @@ -12,6 +12,8 @@ #ifndef __LINUX_PINCTRL_MACHINE_H #define __LINUX_PINCTRL_MACHINE_H +#include "pinctrl.h" + /** * struct pinctrl_map - boards/machines shall provide this map for devices * @dev_name: the name of the device using this specific mapping, the name @@ -49,17 +51,18 @@ struct pinctrl_map { * Convenience macro to map a system function onto a certain pinctrl device, * to be hogged by the pin control core until the system shuts down. */ -#define PIN_MAP_SYS_HOG(a, b, c) \ - { .name = a, .ctrl_dev_name = b, .dev_name = b, .function = c, } +#define PIN_MAP_SYS_HOG(a, b) \ + { .name = PINCTRL_STATE_DEFAULT, .ctrl_dev_name = a, .dev_name = a, \ + .function = b, } /* * Convenience macro to map a system function onto a certain pinctrl device * using a specified group, to be hogged by the pin control core until the * system shuts down. */ -#define PIN_MAP_SYS_HOG_GROUP(a, b, c, d) \ - { .name = a, .ctrl_dev_name = b, .dev_name = b, .function = c, \ - .group = d, } +#define PIN_MAP_SYS_HOG_GROUP(a, b, c) \ + { .name = PINCTRL_STATE_DEFAULT, .ctrl_dev_name = a, .dev_name = a, \ + .function = b, .group = c, } #ifdef CONFIG_PINMUX diff --git a/include/linux/pinctrl/pinctrl.h b/include/linux/pinctrl/pinctrl.h index 8bd22ee7aa09..411fe232adf1 100644 --- a/include/linux/pinctrl/pinctrl.h +++ b/include/linux/pinctrl/pinctrl.h @@ -19,6 +19,8 @@ #include #include +#define PINCTRL_STATE_DEFAULT "default" + struct pinctrl_dev; struct pinmux_ops; struct pinconf_ops; -- cgit v1.2.3 From 2446ab6070861aba2dd9229463ffbc40016a9f33 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Thu, 1 Mar 2012 17:00:56 -0500 Subject: SUNRPC: Use RCU to dereference the rpc_clnt.cl_xprt field A migration event will replace the rpc_xprt used by an rpc_clnt. To ensure this can be done safely, all references to cl_xprt must now use a form of rcu_dereference(). Special care is taken with rpc_peeraddr2str(), which returns a pointer to memory whose lifetime is the same as the rpc_xprt. Signed-off-by: Trond Myklebust [ cel: fix lockdep splats and layering violations ] [ cel: forward ported to 3.4 ] [ cel: remove rpc_max_reqs(), add rpc_net_ns() ] Signed-off-by: Chuck Lever Signed-off-by: Trond Myklebust --- fs/nfs/callback_proc.c | 9 ++-- fs/nfs/client.c | 16 ++++-- fs/nfs/nfs4namespace.c | 2 +- fs/nfs/nfs4proc.c | 13 +++-- fs/nfs/nfs4state.c | 25 +++++++--- fs/nfs/super.c | 5 ++ include/linux/sunrpc/clnt.h | 4 +- include/linux/sunrpc/debug.h | 13 +++++ net/sunrpc/auth_gss/auth_gss.c | 4 +- net/sunrpc/clnt.c | 110 +++++++++++++++++++++++++++++++++-------- net/sunrpc/rpc_pipe.c | 3 ++ net/sunrpc/rpcb_clnt.c | 15 ++++-- net/sunrpc/stats.c | 6 ++- 13 files changed, 175 insertions(+), 50 deletions(-) (limited to 'include/linux') diff --git a/fs/nfs/callback_proc.c b/fs/nfs/callback_proc.c index 0e0865e38065..1bb297243624 100644 --- a/fs/nfs/callback_proc.c +++ b/fs/nfs/callback_proc.c @@ -8,6 +8,7 @@ #include #include #include +#include #include "nfs4_fs.h" #include "callback.h" #include "delegation.h" @@ -33,7 +34,7 @@ __be32 nfs4_callback_getattr(struct cb_getattrargs *args, res->bitmap[0] = res->bitmap[1] = 0; res->status = htonl(NFS4ERR_BADHANDLE); - dprintk("NFS: GETATTR callback request from %s\n", + dprintk_rcu("NFS: GETATTR callback request from %s\n", rpc_peeraddr2str(cps->clp->cl_rpcclient, RPC_DISPLAY_ADDR)); inode = nfs_delegation_find_inode(cps->clp, &args->fh); @@ -73,7 +74,7 @@ __be32 nfs4_callback_recall(struct cb_recallargs *args, void *dummy, if (!cps->clp) /* Always set for v4.0. Set in cb_sequence for v4.1 */ goto out; - dprintk("NFS: RECALL callback request from %s\n", + dprintk_rcu("NFS: RECALL callback request from %s\n", rpc_peeraddr2str(cps->clp->cl_rpcclient, RPC_DISPLAY_ADDR)); res = htonl(NFS4ERR_BADHANDLE); @@ -533,7 +534,7 @@ __be32 nfs4_callback_recallany(struct cb_recallanyargs *args, void *dummy, if (!cps->clp) /* set in cb_sequence */ goto out; - dprintk("NFS: RECALL_ANY callback request from %s\n", + dprintk_rcu("NFS: RECALL_ANY callback request from %s\n", rpc_peeraddr2str(cps->clp->cl_rpcclient, RPC_DISPLAY_ADDR)); status = cpu_to_be32(NFS4ERR_INVAL); @@ -568,7 +569,7 @@ __be32 nfs4_callback_recallslot(struct cb_recallslotargs *args, void *dummy, if (!cps->clp) /* set in cb_sequence */ goto out; - dprintk("NFS: CB_RECALL_SLOT request from %s target max slots %d\n", + dprintk_rcu("NFS: CB_RECALL_SLOT request from %s target max slots %d\n", rpc_peeraddr2str(cps->clp->cl_rpcclient, RPC_DISPLAY_ADDR), args->crsa_target_max_slots); diff --git a/fs/nfs/client.c b/fs/nfs/client.c index 1506adf4d4ed..d038dc5916e5 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -1284,16 +1284,18 @@ static int nfs4_init_callback(struct nfs_client *clp) int error; if (clp->rpc_ops->version == 4) { + struct rpc_xprt *xprt; + + xprt = rcu_dereference_raw(clp->cl_rpcclient->cl_xprt); + if (nfs4_has_session(clp)) { - error = xprt_setup_backchannel( - clp->cl_rpcclient->cl_xprt, + error = xprt_setup_backchannel(xprt, NFS41_BC_MIN_CALLBACKS); if (error < 0) return error; } - error = nfs_callback_up(clp->cl_mvops->minor_version, - clp->cl_rpcclient->cl_xprt); + error = nfs_callback_up(clp->cl_mvops->minor_version, xprt); if (error < 0) { dprintk("%s: failed to start callback. Error = %d\n", __func__, error); @@ -1678,7 +1680,7 @@ struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data, data->addrlen, parent_client->cl_ipaddr, data->authflavor, - parent_server->client->cl_xprt->prot, + rpc_protocol(parent_server->client), parent_server->client->cl_timeout, parent_client->cl_mvops->minor_version, parent_client->net); @@ -1905,12 +1907,14 @@ static int nfs_server_list_show(struct seq_file *m, void *v) if (clp->cl_cons_state != NFS_CS_READY) return 0; + rcu_read_lock(); seq_printf(m, "v%u %s %s %3d %s\n", clp->rpc_ops->version, rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_ADDR), rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_PORT), atomic_read(&clp->cl_count), clp->cl_hostname); + rcu_read_unlock(); return 0; } @@ -1993,6 +1997,7 @@ static int nfs_volume_list_show(struct seq_file *m, void *v) (unsigned long long) server->fsid.major, (unsigned long long) server->fsid.minor); + rcu_read_lock(); seq_printf(m, "v%u %s %s %-7s %-17s %s\n", clp->rpc_ops->version, rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_ADDR), @@ -2000,6 +2005,7 @@ static int nfs_volume_list_show(struct seq_file *m, void *v) dev, fsid, nfs_server_fscache_state(server)); + rcu_read_unlock(); return 0; } diff --git a/fs/nfs/nfs4namespace.c b/fs/nfs/nfs4namespace.c index 667ea7406fd3..9c8eca315f43 100644 --- a/fs/nfs/nfs4namespace.c +++ b/fs/nfs/nfs4namespace.c @@ -96,8 +96,8 @@ static int nfs4_validate_fspath(struct dentry *dentry, static size_t nfs_parse_server_name(char *string, size_t len, struct sockaddr *sa, size_t salen, struct nfs_server *server) { + struct net *net = rpc_net_ns(server->client); ssize_t ret; - struct net *net = server->client->cl_xprt->xprt_net; ret = rpc_pton(net, string, len, sa, salen); if (ret == 0) { diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 6c8e170e2e6b..671510cc14c0 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -3833,6 +3833,7 @@ int nfs4_proc_setclientid(struct nfs_client *clp, u32 program, *p = htonl((u32)clp->cl_boot_time.tv_nsec); for(;;) { + rcu_read_lock(); setclientid.sc_name_len = scnprintf(setclientid.sc_name, sizeof(setclientid.sc_name), "%s/%s %s %s %u", clp->cl_ipaddr, @@ -3849,6 +3850,7 @@ int nfs4_proc_setclientid(struct nfs_client *clp, u32 program, setclientid.sc_uaddr_len = scnprintf(setclientid.sc_uaddr, sizeof(setclientid.sc_uaddr), "%s.%u.%u", clp->cl_ipaddr, port >> 8, port & 255); + rcu_read_unlock(); status = rpc_call_sync(clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT); if (status != -NFS4ERR_CLID_INUSE) @@ -5244,11 +5246,16 @@ struct nfs4_session *nfs4_alloc_session(struct nfs_client *clp) void nfs4_destroy_session(struct nfs4_session *session) { + struct rpc_xprt *xprt; + nfs4_proc_destroy_session(session); + + rcu_read_lock(); + xprt = rcu_dereference(session->clp->cl_rpcclient->cl_xprt); + rcu_read_unlock(); dprintk("%s Destroy backchannel for xprt %p\n", - __func__, session->clp->cl_rpcclient->cl_xprt); - xprt_destroy_backchannel(session->clp->cl_rpcclient->cl_xprt, - NFS41_BC_MIN_CALLBACKS); + __func__, xprt); + xprt_destroy_backchannel(xprt, NFS41_BC_MIN_CALLBACKS); nfs4_destroy_slot_tables(session); kfree(session); } diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index c1111a37dc14..bae959e294cd 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -1037,19 +1037,28 @@ static void nfs4_clear_state_manager_bit(struct nfs_client *clp) void nfs4_schedule_state_manager(struct nfs_client *clp) { struct task_struct *task; + char buf[INET6_ADDRSTRLEN + sizeof("-manager") + 1]; if (test_and_set_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state) != 0) return; __module_get(THIS_MODULE); atomic_inc(&clp->cl_count); - task = kthread_run(nfs4_run_state_manager, clp, "%s-manager", - rpc_peeraddr2str(clp->cl_rpcclient, - RPC_DISPLAY_ADDR)); - if (!IS_ERR(task)) - return; - nfs4_clear_state_manager_bit(clp); - nfs_put_client(clp); - module_put(THIS_MODULE); + + /* The rcu_read_lock() is not strictly necessary, as the state + * manager is the only thread that ever changes the rpc_xprt + * after it's initialized. At this point, we're single threaded. */ + rcu_read_lock(); + snprintf(buf, sizeof(buf), "%s-manager", + rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_ADDR)); + rcu_read_unlock(); + task = kthread_run(nfs4_run_state_manager, clp, buf); + if (IS_ERR(task)) { + printk(KERN_ERR "%s: kthread_run: %ld\n", + __func__, PTR_ERR(task)); + nfs4_clear_state_manager_bit(clp); + nfs_put_client(clp); + module_put(THIS_MODULE); + } } /* diff --git a/fs/nfs/super.c b/fs/nfs/super.c index f4ccdae6a0cf..7002be11d99f 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -53,6 +53,7 @@ #include #include #include +#include #include #include @@ -701,8 +702,10 @@ static void nfs_show_mount_options(struct seq_file *m, struct nfs_server *nfss, else seq_puts(m, nfs_infop->nostr); } + rcu_read_lock(); seq_printf(m, ",proto=%s", rpc_peeraddr2str(nfss->client, RPC_DISPLAY_NETID)); + rcu_read_unlock(); if (version == 4) { if (nfss->port != NFS_PORT) seq_printf(m, ",port=%u", nfss->port); @@ -751,9 +754,11 @@ static int nfs_show_options(struct seq_file *m, struct dentry *root) nfs_show_mount_options(m, nfss, 0); + rcu_read_lock(); seq_printf(m, ",addr=%s", rpc_peeraddr2str(nfss->nfs_client->cl_rpcclient, RPC_DISPLAY_ADDR)); + rcu_read_unlock(); return 0; } diff --git a/include/linux/sunrpc/clnt.h b/include/linux/sunrpc/clnt.h index a4c62e95c720..e3d12b4a0314 100644 --- a/include/linux/sunrpc/clnt.h +++ b/include/linux/sunrpc/clnt.h @@ -35,7 +35,7 @@ struct rpc_clnt { struct list_head cl_clients; /* Global list of clients */ struct list_head cl_tasks; /* List of tasks */ spinlock_t cl_lock; /* spinlock */ - struct rpc_xprt * cl_xprt; /* transport */ + struct rpc_xprt __rcu * cl_xprt; /* transport */ struct rpc_procinfo * cl_procinfo; /* procedure info */ u32 cl_prog, /* RPC program number */ cl_vers, /* RPC version number */ @@ -156,6 +156,8 @@ struct rpc_task *rpc_call_null(struct rpc_clnt *clnt, struct rpc_cred *cred, int rpc_restart_call_prepare(struct rpc_task *); int rpc_restart_call(struct rpc_task *); void rpc_setbufsize(struct rpc_clnt *, unsigned int, unsigned int); +int rpc_protocol(struct rpc_clnt *); +struct net * rpc_net_ns(struct rpc_clnt *); size_t rpc_max_payload(struct rpc_clnt *); void rpc_force_rebind(struct rpc_clnt *); size_t rpc_peeraddr(struct rpc_clnt *, struct sockaddr *, size_t); diff --git a/include/linux/sunrpc/debug.h b/include/linux/sunrpc/debug.h index b506936f4ce6..6cb2517bcf75 100644 --- a/include/linux/sunrpc/debug.h +++ b/include/linux/sunrpc/debug.h @@ -50,19 +50,32 @@ extern unsigned int nlm_debug; #endif #define dprintk(args...) dfprintk(FACILITY, ## args) +#define dprintk_rcu(args...) dfprintk_rcu(FACILITY, ## args) #undef ifdebug #ifdef RPC_DEBUG # define ifdebug(fac) if (unlikely(rpc_debug & RPCDBG_##fac)) + # define dfprintk(fac, args...) \ do { \ ifdebug(fac) \ printk(KERN_DEFAULT args); \ } while (0) + +# define dfprintk_rcu(fac, args...) \ + do { \ + ifdebug(fac) { \ + rcu_read_lock(); \ + printk(KERN_DEFAULT args); \ + rcu_read_unlock(); \ + } \ + } while (0) + # define RPC_IFDEBUG(x) x #else # define ifdebug(fac) if (0) # define dfprintk(fac, args...) do ; while (0) +# define dfprintk_rcu(fac, args...) do ; while (0) # define RPC_IFDEBUG(x) #endif diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index cb2e56452748..d3ad81f8da5b 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c @@ -799,7 +799,7 @@ err_unlink_pipe_1: static void gss_pipes_dentries_destroy_net(struct rpc_clnt *clnt, struct rpc_auth *auth) { - struct net *net = clnt->cl_xprt->xprt_net; + struct net *net = rpc_net_ns(clnt); struct super_block *sb; sb = rpc_get_sb_net(net); @@ -813,7 +813,7 @@ static void gss_pipes_dentries_destroy_net(struct rpc_clnt *clnt, static int gss_pipes_dentries_create_net(struct rpc_clnt *clnt, struct rpc_auth *auth) { - struct net *net = clnt->cl_xprt->xprt_net; + struct net *net = rpc_net_ns(clnt); struct super_block *sb; int err = 0; diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index 25c3da53fb69..7783fc0e7263 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -81,7 +82,8 @@ static int rpc_ping(struct rpc_clnt *clnt); static void rpc_register_client(struct rpc_clnt *clnt) { - struct sunrpc_net *sn = net_generic(clnt->cl_xprt->xprt_net, sunrpc_net_id); + struct net *net = rpc_net_ns(clnt); + struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); spin_lock(&sn->rpc_client_lock); list_add(&clnt->cl_clients, &sn->all_clients); @@ -90,7 +92,8 @@ static void rpc_register_client(struct rpc_clnt *clnt) static void rpc_unregister_client(struct rpc_clnt *clnt) { - struct sunrpc_net *sn = net_generic(clnt->cl_xprt->xprt_net, sunrpc_net_id); + struct net *net = rpc_net_ns(clnt); + struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); spin_lock(&sn->rpc_client_lock); list_del(&clnt->cl_clients); @@ -109,12 +112,13 @@ static void __rpc_clnt_remove_pipedir(struct rpc_clnt *clnt) static void rpc_clnt_remove_pipedir(struct rpc_clnt *clnt) { + struct net *net = rpc_net_ns(clnt); struct super_block *pipefs_sb; - pipefs_sb = rpc_get_sb_net(clnt->cl_xprt->xprt_net); + pipefs_sb = rpc_get_sb_net(net); if (pipefs_sb) { __rpc_clnt_remove_pipedir(clnt); - rpc_put_sb_net(clnt->cl_xprt->xprt_net); + rpc_put_sb_net(net); } } @@ -155,17 +159,18 @@ static struct dentry *rpc_setup_pipedir_sb(struct super_block *sb, static int rpc_setup_pipedir(struct rpc_clnt *clnt, const char *dir_name) { + struct net *net = rpc_net_ns(clnt); struct super_block *pipefs_sb; struct dentry *dentry; clnt->cl_dentry = NULL; if (dir_name == NULL) return 0; - pipefs_sb = rpc_get_sb_net(clnt->cl_xprt->xprt_net); + pipefs_sb = rpc_get_sb_net(net); if (!pipefs_sb) return 0; dentry = rpc_setup_pipedir_sb(pipefs_sb, clnt, dir_name); - rpc_put_sb_net(clnt->cl_xprt->xprt_net); + rpc_put_sb_net(net); if (IS_ERR(dentry)) return PTR_ERR(dentry); clnt->cl_dentry = dentry; @@ -295,7 +300,7 @@ static struct rpc_clnt * rpc_new_client(const struct rpc_create_args *args, stru if (clnt->cl_server == NULL) goto out_no_server; - clnt->cl_xprt = xprt; + rcu_assign_pointer(clnt->cl_xprt, xprt); clnt->cl_procinfo = version->procs; clnt->cl_maxproc = version->nrprocs; clnt->cl_protname = program->name; @@ -310,7 +315,7 @@ static struct rpc_clnt * rpc_new_client(const struct rpc_create_args *args, stru INIT_LIST_HEAD(&clnt->cl_tasks); spin_lock_init(&clnt->cl_lock); - if (!xprt_bound(clnt->cl_xprt)) + if (!xprt_bound(xprt)) clnt->cl_autobind = 1; clnt->cl_timeout = xprt->timeout; @@ -477,6 +482,7 @@ struct rpc_clnt * rpc_clone_client(struct rpc_clnt *clnt) { struct rpc_clnt *new; + struct rpc_xprt *xprt; int err = -ENOMEM; new = kmemdup(clnt, sizeof(*new), GFP_KERNEL); @@ -499,18 +505,25 @@ rpc_clone_client(struct rpc_clnt *clnt) if (new->cl_principal == NULL) goto out_no_principal; } + rcu_read_lock(); + xprt = xprt_get(rcu_dereference(clnt->cl_xprt)); + rcu_read_unlock(); + if (xprt == NULL) + goto out_no_transport; + rcu_assign_pointer(new->cl_xprt, xprt); atomic_set(&new->cl_count, 1); err = rpc_setup_pipedir(new, clnt->cl_program->pipe_dir_name); if (err != 0) goto out_no_path; if (new->cl_auth) atomic_inc(&new->cl_auth->au_count); - xprt_get(clnt->cl_xprt); atomic_inc(&clnt->cl_count); rpc_register_client(new); rpciod_up(); return new; out_no_path: + xprt_put(xprt); +out_no_transport: kfree(new->cl_principal); out_no_principal: rpc_free_iostats(new->cl_metrics); @@ -590,7 +603,7 @@ rpc_free_client(struct rpc_clnt *clnt) rpc_free_iostats(clnt->cl_metrics); kfree(clnt->cl_principal); clnt->cl_metrics = NULL; - xprt_put(clnt->cl_xprt); + xprt_put(rcu_dereference_raw(clnt->cl_xprt)); rpciod_down(); kfree(clnt); } @@ -879,13 +892,18 @@ EXPORT_SYMBOL_GPL(rpc_call_start); size_t rpc_peeraddr(struct rpc_clnt *clnt, struct sockaddr *buf, size_t bufsize) { size_t bytes; - struct rpc_xprt *xprt = clnt->cl_xprt; + struct rpc_xprt *xprt; + + rcu_read_lock(); + xprt = rcu_dereference(clnt->cl_xprt); - bytes = sizeof(xprt->addr); + bytes = xprt->addrlen; if (bytes > bufsize) bytes = bufsize; - memcpy(buf, &clnt->cl_xprt->addr, bytes); - return xprt->addrlen; + memcpy(buf, &xprt->addr, bytes); + rcu_read_unlock(); + + return bytes; } EXPORT_SYMBOL_GPL(rpc_peeraddr); @@ -894,11 +912,16 @@ EXPORT_SYMBOL_GPL(rpc_peeraddr); * @clnt: RPC client structure * @format: address format * + * NB: the lifetime of the memory referenced by the returned pointer is + * the same as the rpc_xprt itself. As long as the caller uses this + * pointer, it must hold the RCU read lock. */ const char *rpc_peeraddr2str(struct rpc_clnt *clnt, enum rpc_display_format_t format) { - struct rpc_xprt *xprt = clnt->cl_xprt; + struct rpc_xprt *xprt; + + xprt = rcu_dereference(clnt->cl_xprt); if (xprt->address_strings[format] != NULL) return xprt->address_strings[format]; @@ -910,14 +933,51 @@ EXPORT_SYMBOL_GPL(rpc_peeraddr2str); void rpc_setbufsize(struct rpc_clnt *clnt, unsigned int sndsize, unsigned int rcvsize) { - struct rpc_xprt *xprt = clnt->cl_xprt; + struct rpc_xprt *xprt; + + rcu_read_lock(); + xprt = rcu_dereference(clnt->cl_xprt); if (xprt->ops->set_buffer_size) xprt->ops->set_buffer_size(xprt, sndsize, rcvsize); + rcu_read_unlock(); } EXPORT_SYMBOL_GPL(rpc_setbufsize); -/* - * Return size of largest payload RPC client can support, in bytes +/** + * rpc_protocol - Get transport protocol number for an RPC client + * @clnt: RPC client to query + * + */ +int rpc_protocol(struct rpc_clnt *clnt) +{ + int protocol; + + rcu_read_lock(); + protocol = rcu_dereference(clnt->cl_xprt)->prot; + rcu_read_unlock(); + return protocol; +} +EXPORT_SYMBOL_GPL(rpc_protocol); + +/** + * rpc_net_ns - Get the network namespace for this RPC client + * @clnt: RPC client to query + * + */ +struct net *rpc_net_ns(struct rpc_clnt *clnt) +{ + struct net *ret; + + rcu_read_lock(); + ret = rcu_dereference(clnt->cl_xprt)->xprt_net; + rcu_read_unlock(); + return ret; +} +EXPORT_SYMBOL_GPL(rpc_net_ns); + +/** + * rpc_max_payload - Get maximum payload size for a transport, in bytes + * @clnt: RPC client to query * * For stream transports, this is one RPC record fragment (see RFC * 1831), as we don't support multi-record requests yet. For datagram @@ -926,7 +986,12 @@ EXPORT_SYMBOL_GPL(rpc_setbufsize); */ size_t rpc_max_payload(struct rpc_clnt *clnt) { - return clnt->cl_xprt->max_payload; + size_t ret; + + rcu_read_lock(); + ret = rcu_dereference(clnt->cl_xprt)->max_payload; + rcu_read_unlock(); + return ret; } EXPORT_SYMBOL_GPL(rpc_max_payload); @@ -937,8 +1002,11 @@ EXPORT_SYMBOL_GPL(rpc_max_payload); */ void rpc_force_rebind(struct rpc_clnt *clnt) { - if (clnt->cl_autobind) - xprt_clear_bound(clnt->cl_xprt); + if (clnt->cl_autobind) { + rcu_read_lock(); + xprt_clear_bound(rcu_dereference(clnt->cl_xprt)); + rcu_read_unlock(); + } } EXPORT_SYMBOL_GPL(rpc_force_rebind); diff --git a/net/sunrpc/rpc_pipe.c b/net/sunrpc/rpc_pipe.c index ac9ee1590739..3d30943ed6db 100644 --- a/net/sunrpc/rpc_pipe.c +++ b/net/sunrpc/rpc_pipe.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -402,12 +403,14 @@ rpc_show_info(struct seq_file *m, void *v) { struct rpc_clnt *clnt = m->private; + rcu_read_lock(); seq_printf(m, "RPC server: %s\n", clnt->cl_server); seq_printf(m, "service: %s (%d) version %d\n", clnt->cl_protname, clnt->cl_prog, clnt->cl_vers); seq_printf(m, "address: %s\n", rpc_peeraddr2str(clnt, RPC_DISPLAY_ADDR)); seq_printf(m, "protocol: %s\n", rpc_peeraddr2str(clnt, RPC_DISPLAY_PROTO)); seq_printf(m, "port: %s\n", rpc_peeraddr2str(clnt, RPC_DISPLAY_PORT)); + rcu_read_unlock(); return 0; } diff --git a/net/sunrpc/rpcb_clnt.c b/net/sunrpc/rpcb_clnt.c index b1f08bd67883..4f8af63798a2 100644 --- a/net/sunrpc/rpcb_clnt.c +++ b/net/sunrpc/rpcb_clnt.c @@ -620,9 +620,10 @@ static struct rpc_task *rpcb_call_async(struct rpc_clnt *rpcb_clnt, struct rpcbi static struct rpc_clnt *rpcb_find_transport_owner(struct rpc_clnt *clnt) { struct rpc_clnt *parent = clnt->cl_parent; + struct rpc_xprt *xprt = rcu_dereference(clnt->cl_xprt); while (parent != clnt) { - if (parent->cl_xprt != clnt->cl_xprt) + if (rcu_dereference(parent->cl_xprt) != xprt) break; if (clnt->cl_autobind) break; @@ -653,8 +654,12 @@ void rpcb_getport_async(struct rpc_task *task) size_t salen; int status; - clnt = rpcb_find_transport_owner(task->tk_client); - xprt = clnt->cl_xprt; + rcu_read_lock(); + do { + clnt = rpcb_find_transport_owner(task->tk_client); + xprt = xprt_get(rcu_dereference(clnt->cl_xprt)); + } while (xprt == NULL); + rcu_read_unlock(); dprintk("RPC: %5u %s(%s, %u, %u, %d)\n", task->tk_pid, __func__, @@ -667,6 +672,7 @@ void rpcb_getport_async(struct rpc_task *task) if (xprt_test_and_set_binding(xprt)) { dprintk("RPC: %5u %s: waiting for another binder\n", task->tk_pid, __func__); + xprt_put(xprt); return; } @@ -734,7 +740,7 @@ void rpcb_getport_async(struct rpc_task *task) switch (bind_version) { case RPCBVERS_4: case RPCBVERS_3: - map->r_netid = rpc_peeraddr2str(clnt, RPC_DISPLAY_NETID); + map->r_netid = xprt->address_strings[RPC_DISPLAY_NETID]; map->r_addr = rpc_sockaddr2uaddr(sap, GFP_ATOMIC); map->r_owner = ""; break; @@ -763,6 +769,7 @@ bailout_release_client: bailout_nofree: rpcb_wake_rpcbind_waiters(xprt, status); task->tk_status = status; + xprt_put(xprt); } EXPORT_SYMBOL_GPL(rpcb_getport_async); diff --git a/net/sunrpc/stats.c b/net/sunrpc/stats.c index 1eb3304bc105..bc2068ee795b 100644 --- a/net/sunrpc/stats.c +++ b/net/sunrpc/stats.c @@ -22,6 +22,7 @@ #include #include #include +#include #include "netns.h" @@ -179,7 +180,7 @@ static void _print_name(struct seq_file *seq, unsigned int op, void rpc_print_iostats(struct seq_file *seq, struct rpc_clnt *clnt) { struct rpc_iostats *stats = clnt->cl_metrics; - struct rpc_xprt *xprt = clnt->cl_xprt; + struct rpc_xprt *xprt; unsigned int op, maxproc = clnt->cl_maxproc; if (!stats) @@ -189,8 +190,11 @@ void rpc_print_iostats(struct seq_file *seq, struct rpc_clnt *clnt) seq_printf(seq, "p/v: %u/%u (%s)\n", clnt->cl_prog, clnt->cl_vers, clnt->cl_protname); + rcu_read_lock(); + xprt = rcu_dereference(clnt->cl_xprt); if (xprt) xprt->ops->print_stats(xprt, seq); + rcu_read_unlock(); seq_printf(seq, "\tper-op statistics\n"); for (op = 0; op < maxproc; op++) { -- cgit v1.2.3 From 4e0038b6b246e4145fc4a53dca61a556d17bc52c Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Thu, 1 Mar 2012 17:01:05 -0500 Subject: SUNRPC: Move clnt->cl_server into struct rpc_xprt When the cl_xprt field is updated, the cl_server field will also have to change. Since the contents of cl_server follow the remote endpoint of cl_xprt, just move that field to the rpc_xprt. Signed-off-by: Trond Myklebust [ cel: simplify check_gss_callback_principal(), whitespace changes ] [ cel: forward ported to 3.4 ] Signed-off-by: Chuck Lever Signed-off-by: Trond Myklebust --- fs/nfs/callback.c | 3 +- fs/nfs/nfs4proc.c | 3 +- include/linux/sunrpc/clnt.h | 1 - include/linux/sunrpc/xprt.h | 2 ++ net/sunrpc/clnt.c | 88 ++++++++++++++++++++++----------------------- net/sunrpc/rpc_pipe.c | 3 +- net/sunrpc/rpcb_clnt.c | 4 +-- net/sunrpc/xprt.c | 15 +++++++- 8 files changed, 66 insertions(+), 53 deletions(-) (limited to 'include/linux') diff --git a/fs/nfs/callback.c b/fs/nfs/callback.c index 4a122ae71762..2afe23349c7b 100644 --- a/fs/nfs/callback.c +++ b/fs/nfs/callback.c @@ -332,7 +332,6 @@ void nfs_callback_down(int minorversion) int check_gss_callback_principal(struct nfs_client *clp, struct svc_rqst *rqstp) { - struct rpc_clnt *r = clp->cl_rpcclient; char *p = svc_gss_principal(rqstp); if (rqstp->rq_authop->flavour != RPC_AUTH_GSS) @@ -353,7 +352,7 @@ check_gss_callback_principal(struct nfs_client *clp, struct svc_rqst *rqstp) if (memcmp(p, "nfs@", 4) != 0) return 0; p += 4; - if (strcmp(p, r->cl_server) != 0) + if (strcmp(p, clp->cl_hostname) != 0) return 0; return 1; } diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 671510cc14c0..54767dd66cf9 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -1100,6 +1100,7 @@ static struct nfs4_state *nfs4_opendata_to_nfs4_state(struct nfs4_opendata *data if (state == NULL) goto err_put_inode; if (data->o_res.delegation_type != 0) { + struct nfs_client *clp = NFS_SERVER(inode)->nfs_client; int delegation_flags = 0; rcu_read_lock(); @@ -1111,7 +1112,7 @@ static struct nfs4_state *nfs4_opendata_to_nfs4_state(struct nfs4_opendata *data pr_err_ratelimited("NFS: Broken NFSv4 server %s is " "returning a delegation for " "OPEN(CLAIM_DELEGATE_CUR)\n", - NFS_CLIENT(inode)->cl_server); + clp->cl_hostname); } else if ((delegation_flags & 1UL<inode, data->owner->so_cred, diff --git a/include/linux/sunrpc/clnt.h b/include/linux/sunrpc/clnt.h index e3d12b4a0314..acd5502a8190 100644 --- a/include/linux/sunrpc/clnt.h +++ b/include/linux/sunrpc/clnt.h @@ -41,7 +41,6 @@ struct rpc_clnt { cl_vers, /* RPC version number */ cl_maxproc; /* max procedure number */ - const char * cl_server; /* server machine name */ const char * cl_protname; /* protocol name */ struct rpc_auth * cl_auth; /* authenticator */ struct rpc_stat * cl_stats; /* per-program statistics */ diff --git a/include/linux/sunrpc/xprt.h b/include/linux/sunrpc/xprt.h index ea712f97f4a1..77d278defa70 100644 --- a/include/linux/sunrpc/xprt.h +++ b/include/linux/sunrpc/xprt.h @@ -229,6 +229,7 @@ struct rpc_xprt { } stat; struct net *xprt_net; + const char *servername; const char *address_strings[RPC_DISPLAY_MAX]; }; @@ -258,6 +259,7 @@ struct xprt_create { struct sockaddr * srcaddr; /* optional local address */ struct sockaddr * dstaddr; /* remote peer address */ size_t addrlen; + const char *servername; struct svc_xprt *bc_xprt; /* NFSv4.1 backchannel */ }; diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index 7783fc0e7263..e39ace9a4e1d 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -265,15 +265,8 @@ static struct rpc_clnt * rpc_new_client(const struct rpc_create_args *args, stru struct rpc_clnt *clnt = NULL; struct rpc_auth *auth; int err; - size_t len; /* sanity check the name before trying to print it */ - err = -EINVAL; - len = strlen(args->servername); - if (len > RPC_MAXNETNAMELEN) - goto out_no_rpciod; - len++; - dprintk("RPC: creating %s client for %s (xprt %p)\n", program->name, args->servername, xprt); @@ -296,10 +289,6 @@ static struct rpc_clnt * rpc_new_client(const struct rpc_create_args *args, stru goto out_err; clnt->cl_parent = clnt; - clnt->cl_server = kstrdup(args->servername, GFP_KERNEL); - if (clnt->cl_server == NULL) - goto out_no_server; - rcu_assign_pointer(clnt->cl_xprt, xprt); clnt->cl_procinfo = version->procs; clnt->cl_maxproc = version->nrprocs; @@ -363,8 +352,6 @@ out_no_path: out_no_principal: rpc_free_iostats(clnt->cl_metrics); out_no_stats: - kfree(clnt->cl_server); -out_no_server: kfree(clnt); out_err: xprt_put(xprt); @@ -394,6 +381,7 @@ struct rpc_clnt *rpc_create(struct rpc_create_args *args) .srcaddr = args->saddress, .dstaddr = args->address, .addrlen = args->addrsize, + .servername = args->servername, .bc_xprt = args->bc_xprt, }; char servername[48]; @@ -402,7 +390,7 @@ struct rpc_clnt *rpc_create(struct rpc_create_args *args) * If the caller chooses not to specify a hostname, whip * up a string representation of the passed-in address. */ - if (args->servername == NULL) { + if (xprtargs.servername == NULL) { struct sockaddr_un *sun = (struct sockaddr_un *)args->address; struct sockaddr_in *sin = @@ -429,7 +417,7 @@ struct rpc_clnt *rpc_create(struct rpc_create_args *args) * address family isn't recognized. */ return ERR_PTR(-EINVAL); } - args->servername = servername; + xprtargs.servername = servername; } xprt = xprt_create_transport(&xprtargs); @@ -488,9 +476,6 @@ rpc_clone_client(struct rpc_clnt *clnt) new = kmemdup(clnt, sizeof(*new), GFP_KERNEL); if (!new) goto out_no_clnt; - new->cl_server = kstrdup(clnt->cl_server, GFP_KERNEL); - if (new->cl_server == NULL) - goto out_no_server; new->cl_parent = clnt; /* Turn off autobind on clones */ new->cl_autobind = 0; @@ -528,8 +513,6 @@ out_no_transport: out_no_principal: rpc_free_iostats(new->cl_metrics); out_no_stats: - kfree(new->cl_server); -out_no_server: kfree(new); out_no_clnt: dprintk("RPC: %s: returned error %d\n", __func__, err); @@ -574,8 +557,9 @@ EXPORT_SYMBOL_GPL(rpc_killall_tasks); */ void rpc_shutdown_client(struct rpc_clnt *clnt) { - dprintk("RPC: shutting down %s client for %s\n", - clnt->cl_protname, clnt->cl_server); + dprintk_rcu("RPC: shutting down %s client for %s\n", + clnt->cl_protname, + rcu_dereference(clnt->cl_xprt)->servername); while (!list_empty(&clnt->cl_tasks)) { rpc_killall_tasks(clnt); @@ -593,11 +577,11 @@ EXPORT_SYMBOL_GPL(rpc_shutdown_client); static void rpc_free_client(struct rpc_clnt *clnt) { - dprintk("RPC: destroying %s client for %s\n", - clnt->cl_protname, clnt->cl_server); + dprintk_rcu("RPC: destroying %s client for %s\n", + clnt->cl_protname, + rcu_dereference(clnt->cl_xprt)->servername); if (clnt->cl_parent != clnt) rpc_release_client(clnt->cl_parent); - kfree(clnt->cl_server); rpc_unregister_client(clnt); rpc_clnt_remove_pipedir(clnt); rpc_free_iostats(clnt->cl_metrics); @@ -1685,8 +1669,11 @@ call_timeout(struct rpc_task *task) } if (RPC_IS_SOFT(task)) { if (clnt->cl_chatty) + rcu_read_lock(); printk(KERN_NOTICE "%s: server %s not responding, timed out\n", - clnt->cl_protname, clnt->cl_server); + clnt->cl_protname, + rcu_dereference(clnt->cl_xprt)->servername); + rcu_read_unlock(); if (task->tk_flags & RPC_TASK_TIMEOUT) rpc_exit(task, -ETIMEDOUT); else @@ -1696,9 +1683,13 @@ call_timeout(struct rpc_task *task) if (!(task->tk_flags & RPC_CALL_MAJORSEEN)) { task->tk_flags |= RPC_CALL_MAJORSEEN; - if (clnt->cl_chatty) + if (clnt->cl_chatty) { + rcu_read_lock(); printk(KERN_NOTICE "%s: server %s not responding, still trying\n", - clnt->cl_protname, clnt->cl_server); + clnt->cl_protname, + rcu_dereference(clnt->cl_xprt)->servername); + rcu_read_unlock(); + } } rpc_force_rebind(clnt); /* @@ -1727,9 +1718,13 @@ call_decode(struct rpc_task *task) dprint_status(task); if (task->tk_flags & RPC_CALL_MAJORSEEN) { - if (clnt->cl_chatty) + if (clnt->cl_chatty) { + rcu_read_lock(); printk(KERN_NOTICE "%s: server %s OK\n", - clnt->cl_protname, clnt->cl_server); + clnt->cl_protname, + rcu_dereference(clnt->cl_xprt)->servername); + rcu_read_unlock(); + } task->tk_flags &= ~RPC_CALL_MAJORSEEN; } @@ -1807,6 +1802,7 @@ rpc_encode_header(struct rpc_task *task) static __be32 * rpc_verify_header(struct rpc_task *task) { + struct rpc_clnt *clnt = task->tk_client; struct kvec *iov = &task->tk_rqstp->rq_rcv_buf.head[0]; int len = task->tk_rqstp->rq_rcv_buf.len >> 2; __be32 *p = iov->iov_base; @@ -1879,8 +1875,11 @@ rpc_verify_header(struct rpc_task *task) task->tk_action = call_bind; goto out_retry; case RPC_AUTH_TOOWEAK: + rcu_read_lock(); printk(KERN_NOTICE "RPC: server %s requires stronger " - "authentication.\n", task->tk_client->cl_server); + "authentication.\n", + rcu_dereference(clnt->cl_xprt)->servername); + rcu_read_unlock(); break; default: dprintk("RPC: %5u %s: unknown auth error: %x\n", @@ -1903,28 +1902,27 @@ rpc_verify_header(struct rpc_task *task) case RPC_SUCCESS: return p; case RPC_PROG_UNAVAIL: - dprintk("RPC: %5u %s: program %u is unsupported by server %s\n", - task->tk_pid, __func__, - (unsigned int)task->tk_client->cl_prog, - task->tk_client->cl_server); + dprintk_rcu("RPC: %5u %s: program %u is unsupported " + "by server %s\n", task->tk_pid, __func__, + (unsigned int)clnt->cl_prog, + rcu_dereference(clnt->cl_xprt)->servername); error = -EPFNOSUPPORT; goto out_err; case RPC_PROG_MISMATCH: - dprintk("RPC: %5u %s: program %u, version %u unsupported by " - "server %s\n", task->tk_pid, __func__, - (unsigned int)task->tk_client->cl_prog, - (unsigned int)task->tk_client->cl_vers, - task->tk_client->cl_server); + dprintk_rcu("RPC: %5u %s: program %u, version %u unsupported " + "by server %s\n", task->tk_pid, __func__, + (unsigned int)clnt->cl_prog, + (unsigned int)clnt->cl_vers, + rcu_dereference(clnt->cl_xprt)->servername); error = -EPROTONOSUPPORT; goto out_err; case RPC_PROC_UNAVAIL: - dprintk("RPC: %5u %s: proc %s unsupported by program %u, " + dprintk_rcu("RPC: %5u %s: proc %s unsupported by program %u, " "version %u on server %s\n", task->tk_pid, __func__, rpc_proc_name(task), - task->tk_client->cl_prog, - task->tk_client->cl_vers, - task->tk_client->cl_server); + clnt->cl_prog, clnt->cl_vers, + rcu_dereference(clnt->cl_xprt)->servername); error = -EOPNOTSUPP; goto out_err; case RPC_GARBAGE_ARGS: @@ -1938,7 +1936,7 @@ rpc_verify_header(struct rpc_task *task) } out_garbage: - task->tk_client->cl_stats->rpcgarbage++; + clnt->cl_stats->rpcgarbage++; if (task->tk_garb_retry) { task->tk_garb_retry--; dprintk("RPC: %5u %s: retrying\n", diff --git a/net/sunrpc/rpc_pipe.c b/net/sunrpc/rpc_pipe.c index 3d30943ed6db..7d96e3cd57cc 100644 --- a/net/sunrpc/rpc_pipe.c +++ b/net/sunrpc/rpc_pipe.c @@ -404,7 +404,8 @@ rpc_show_info(struct seq_file *m, void *v) struct rpc_clnt *clnt = m->private; rcu_read_lock(); - seq_printf(m, "RPC server: %s\n", clnt->cl_server); + seq_printf(m, "RPC server: %s\n", + rcu_dereference(clnt->cl_xprt)->servername); seq_printf(m, "service: %s (%d) version %d\n", clnt->cl_protname, clnt->cl_prog, clnt->cl_vers); seq_printf(m, "address: %s\n", rpc_peeraddr2str(clnt, RPC_DISPLAY_ADDR)); diff --git a/net/sunrpc/rpcb_clnt.c b/net/sunrpc/rpcb_clnt.c index 4f8af63798a2..e699ff0ce909 100644 --- a/net/sunrpc/rpcb_clnt.c +++ b/net/sunrpc/rpcb_clnt.c @@ -663,7 +663,7 @@ void rpcb_getport_async(struct rpc_task *task) dprintk("RPC: %5u %s(%s, %u, %u, %d)\n", task->tk_pid, __func__, - clnt->cl_server, clnt->cl_prog, clnt->cl_vers, xprt->prot); + xprt->servername, clnt->cl_prog, clnt->cl_vers, xprt->prot); /* Put self on the wait queue to ensure we get notified if * some other task is already attempting to bind the port */ @@ -714,7 +714,7 @@ void rpcb_getport_async(struct rpc_task *task) dprintk("RPC: %5u %s: trying rpcbind version %u\n", task->tk_pid, __func__, bind_version); - rpcb_clnt = rpcb_create(xprt->xprt_net, clnt->cl_server, sap, salen, + rpcb_clnt = rpcb_create(xprt->xprt_net, xprt->servername, sap, salen, xprt->prot, bind_version); if (IS_ERR(rpcb_clnt)) { status = PTR_ERR(rpcb_clnt); diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c index 32e37945a840..0cbcd1ab49ab 100644 --- a/net/sunrpc/xprt.c +++ b/net/sunrpc/xprt.c @@ -66,6 +66,7 @@ static void xprt_init(struct rpc_xprt *xprt, struct net *net); static void xprt_request_init(struct rpc_task *, struct rpc_xprt *); static void xprt_connect_status(struct rpc_task *task); static int __xprt_get_cong(struct rpc_xprt *, struct rpc_task *); +static void xprt_destroy(struct rpc_xprt *xprt); static DEFINE_SPINLOCK(xprt_list_lock); static LIST_HEAD(xprt_list); @@ -751,7 +752,7 @@ static void xprt_connect_status(struct rpc_task *task) default: dprintk("RPC: %5u xprt_connect_status: error %d connecting to " "server %s\n", task->tk_pid, -task->tk_status, - task->tk_client->cl_server); + xprt->servername); xprt_release_write(xprt, task); task->tk_status = -EIO; } @@ -1229,6 +1230,17 @@ found: (unsigned long)xprt); else init_timer(&xprt->timer); + + if (strlen(args->servername) > RPC_MAXNETNAMELEN) { + xprt_destroy(xprt); + return ERR_PTR(-EINVAL); + } + xprt->servername = kstrdup(args->servername, GFP_KERNEL); + if (xprt->servername == NULL) { + xprt_destroy(xprt); + return ERR_PTR(-ENOMEM); + } + dprintk("RPC: created transport %p with %u slots\n", xprt, xprt->max_reqs); out: @@ -1251,6 +1263,7 @@ static void xprt_destroy(struct rpc_xprt *xprt) rpc_destroy_wait_queue(&xprt->sending); rpc_destroy_wait_queue(&xprt->backlog); cancel_work_sync(&xprt->task_cleanup); + kfree(xprt->servername); /* * Tear down transport state and free the rpc_xprt */ -- cgit v1.2.3 From 2e738fdce22f9a7edf20281fd2d768ef9785922e Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Thu, 1 Mar 2012 17:01:14 -0500 Subject: SUNRPC: Add API to acquire source address NFSv4.0 clients must send endpoint information for their callback service to NFSv4.0 servers during their first contact with a server. Traditionally on Linux, user space provides the callback endpoint IP address via the "clientaddr=" mount option. During an NFSv4 migration event, it is possible that an FSID may be migrated to a destination server that is accessible via a different source IP address than the source server was. The client must update callback endpoint information on the destination server so that it can maintain leases and allow delegation. Without a new "clientaddr=" option from user space, however, the kernel itself must construct an appropriate IP address for the callback update. Provide an API in the RPC client for upper layer RPC consumers to acquire a source address for a remote. The mechanism used by the mount.nfs command is copied: set up a connected UDP socket to the designated remote, then scrape the source address off the socket. We are careful to select the correct network namespace when setting up the temporary UDP socket. Signed-off-by: Chuck Lever Signed-off-by: Trond Myklebust --- include/linux/sunrpc/clnt.h | 1 + net/sunrpc/clnt.c | 149 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 150 insertions(+) (limited to 'include/linux') diff --git a/include/linux/sunrpc/clnt.h b/include/linux/sunrpc/clnt.h index acd5502a8190..523547ecfee2 100644 --- a/include/linux/sunrpc/clnt.h +++ b/include/linux/sunrpc/clnt.h @@ -161,6 +161,7 @@ size_t rpc_max_payload(struct rpc_clnt *); void rpc_force_rebind(struct rpc_clnt *); size_t rpc_peeraddr(struct rpc_clnt *, struct sockaddr *, size_t); const char *rpc_peeraddr2str(struct rpc_clnt *, enum rpc_display_format_t); +int rpc_localaddr(struct rpc_clnt *, struct sockaddr *, size_t); size_t rpc_ntop(const struct sockaddr *, char *, const size_t); size_t rpc_pton(struct net *, const char *, const size_t, diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index e39ace9a4e1d..7a4cb5fdc212 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -914,6 +914,155 @@ const char *rpc_peeraddr2str(struct rpc_clnt *clnt, } EXPORT_SYMBOL_GPL(rpc_peeraddr2str); +static const struct sockaddr_in rpc_inaddr_loopback = { + .sin_family = AF_INET, + .sin_addr.s_addr = htonl(INADDR_ANY), +}; + +static const struct sockaddr_in6 rpc_in6addr_loopback = { + .sin6_family = AF_INET6, + .sin6_addr = IN6ADDR_ANY_INIT, +}; + +/* + * Try a getsockname() on a connected datagram socket. Using a + * connected datagram socket prevents leaving a socket in TIME_WAIT. + * This conserves the ephemeral port number space. + * + * Returns zero and fills in "buf" if successful; otherwise, a + * negative errno is returned. + */ +static int rpc_sockname(struct net *net, struct sockaddr *sap, size_t salen, + struct sockaddr *buf, int buflen) +{ + struct socket *sock; + int err; + + err = __sock_create(net, sap->sa_family, + SOCK_DGRAM, IPPROTO_UDP, &sock, 1); + if (err < 0) { + dprintk("RPC: can't create UDP socket (%d)\n", err); + goto out; + } + + switch (sap->sa_family) { + case AF_INET: + err = kernel_bind(sock, + (struct sockaddr *)&rpc_inaddr_loopback, + sizeof(rpc_inaddr_loopback)); + break; + case AF_INET6: + err = kernel_bind(sock, + (struct sockaddr *)&rpc_in6addr_loopback, + sizeof(rpc_in6addr_loopback)); + break; + default: + err = -EAFNOSUPPORT; + goto out; + } + if (err < 0) { + dprintk("RPC: can't bind UDP socket (%d)\n", err); + goto out_release; + } + + err = kernel_connect(sock, sap, salen, 0); + if (err < 0) { + dprintk("RPC: can't connect UDP socket (%d)\n", err); + goto out_release; + } + + err = kernel_getsockname(sock, buf, &buflen); + if (err < 0) { + dprintk("RPC: getsockname failed (%d)\n", err); + goto out_release; + } + + err = 0; + if (buf->sa_family == AF_INET6) { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)buf; + sin6->sin6_scope_id = 0; + } + dprintk("RPC: %s succeeded\n", __func__); + +out_release: + sock_release(sock); +out: + return err; +} + +/* + * Scraping a connected socket failed, so we don't have a useable + * local address. Fallback: generate an address that will prevent + * the server from calling us back. + * + * Returns zero and fills in "buf" if successful; otherwise, a + * negative errno is returned. + */ +static int rpc_anyaddr(int family, struct sockaddr *buf, size_t buflen) +{ + switch (family) { + case AF_INET: + if (buflen < sizeof(rpc_inaddr_loopback)) + return -EINVAL; + memcpy(buf, &rpc_inaddr_loopback, + sizeof(rpc_inaddr_loopback)); + break; + case AF_INET6: + if (buflen < sizeof(rpc_in6addr_loopback)) + return -EINVAL; + memcpy(buf, &rpc_in6addr_loopback, + sizeof(rpc_in6addr_loopback)); + default: + dprintk("RPC: %s: address family not supported\n", + __func__); + return -EAFNOSUPPORT; + } + dprintk("RPC: %s: succeeded\n", __func__); + return 0; +} + +/** + * rpc_localaddr - discover local endpoint address for an RPC client + * @clnt: RPC client structure + * @buf: target buffer + * @buflen: size of target buffer, in bytes + * + * Returns zero and fills in "buf" and "buflen" if successful; + * otherwise, a negative errno is returned. + * + * This works even if the underlying transport is not currently connected, + * or if the upper layer never previously provided a source address. + * + * The result of this function call is transient: multiple calls in + * succession may give different results, depending on how local + * networking configuration changes over time. + */ +int rpc_localaddr(struct rpc_clnt *clnt, struct sockaddr *buf, size_t buflen) +{ + struct sockaddr_storage address; + struct sockaddr *sap = (struct sockaddr *)&address; + struct rpc_xprt *xprt; + struct net *net; + size_t salen; + int err; + + rcu_read_lock(); + xprt = rcu_dereference(clnt->cl_xprt); + salen = xprt->addrlen; + memcpy(sap, &xprt->addr, salen); + net = get_net(xprt->xprt_net); + rcu_read_unlock(); + + rpc_set_port(sap, 0); + err = rpc_sockname(net, sap, salen, buf, buflen); + put_net(net); + if (err != 0) + /* Couldn't discover local address, return ANYADDR */ + return rpc_anyaddr(sap->sa_family, buf, buflen); + return 0; +} +EXPORT_SYMBOL_GPL(rpc_localaddr); + void rpc_setbufsize(struct rpc_clnt *clnt, unsigned int sndsize, unsigned int rcvsize) { -- cgit v1.2.3 From 20d27e929fb4790a339a4ddcc9a27f14db06055b Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Thu, 1 Mar 2012 17:01:31 -0500 Subject: NFS: Add a client-side function to display NFS file handles For debugging, introduce a simplistic function to print NFS file handles on the system console. The main function is hooked into the dprintk debugging facility, but you can directly call the helper, _nfs_display_fhandle(), if you want to print a handle unconditionally. Signed-off-by: Chuck Lever Signed-off-by: Trond Myklebust --- fs/nfs/inode.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ include/linux/nfs_fs.h | 14 ++++++++++++++ 2 files changed, 59 insertions(+) (limited to 'include/linux') diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 6c662598f885..99a4f52c14b2 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -1045,6 +1045,51 @@ struct nfs_fh *nfs_alloc_fhandle(void) return fh; } +/** + * _nfs_display_fhandle - display an NFS file handle on the console + * + * @fh: file handle to display + * @caption: display caption + * + * For debugging only. + */ +#ifdef RPC_DEBUG +void _nfs_display_fhandle(const struct nfs_fh *fh, const char *caption) +{ + unsigned short i; + + if (fh->size == 0 || fh == NULL) { + printk(KERN_DEFAULT "%s at %p is empty\n", caption, fh); + return; + } + + printk(KERN_DEFAULT "%s at %p is %u bytes:\n", caption, fh, fh->size); + for (i = 0; i < fh->size; i += 16) { + __be32 *pos = (__be32 *)&fh->data[i]; + + switch ((fh->size - i - 1) >> 2) { + case 0: + printk(KERN_DEFAULT " %08x\n", + be32_to_cpup(pos)); + break; + case 1: + printk(KERN_DEFAULT " %08x %08x\n", + be32_to_cpup(pos), be32_to_cpup(pos + 1)); + break; + case 2: + printk(KERN_DEFAULT " %08x %08x %08x\n", + be32_to_cpup(pos), be32_to_cpup(pos + 1), + be32_to_cpup(pos + 2)); + break; + default: + printk(KERN_DEFAULT " %08x %08x %08x %08x\n", + be32_to_cpup(pos), be32_to_cpup(pos + 1), + be32_to_cpup(pos + 2), be32_to_cpup(pos + 3)); + } + } +} +#endif + /** * nfs_inode_attrs_need_update - check if the inode attributes need updating * @inode - pointer to inode diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index 8c29950d2fa5..c07a757649dc 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -395,6 +395,20 @@ static inline void nfs_free_fhandle(const struct nfs_fh *fh) kfree(fh); } +#ifdef RPC_DEBUG +extern void _nfs_display_fhandle(const struct nfs_fh *fh, const char *caption); +#define nfs_display_fhandle(fh, caption) \ + do { \ + if (unlikely(nfs_debug & NFSDBG_FACILITY)) \ + _nfs_display_fhandle(fh, caption); \ + } while (0) +#else +static inline void nfs_display_fhandle(const struct nfs_fh *fh, + const char *caption) +{ +} +#endif + /* * linux/fs/nfs/nfsroot.c */ -- cgit v1.2.3 From 81934ddb8eb62a85b8015c0f2b824a88510965a2 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Thu, 1 Mar 2012 17:01:57 -0500 Subject: NFS: Introduce NFS_ATTR_FATTR_V4_LOCATIONS The Linux NFS client must distinguish between referral events (which it currently supports) and migration events (which it does not yet support). In both types of events, an fs_locations array is returned. But upper layers, not the XDR layer, should make the distinction between a referral and a migration. There really isn't a way for an XDR decoder function to distinguish the two, in general. Slightly adjust the FATTR flags returned by decode_fs_locations() to set NFS_ATTR_FATTR_V4_LOCATIONS only if a non-empty locations array was returned from the server. Then have logic in nfs4proc.c distinguish whether the locations array is for a referral or something else. Signed-off-by: Chuck Lever Signed-off-by: Trond Myklebust --- fs/nfs/nfs4proc.c | 6 +++--- fs/nfs/nfs4xdr.c | 2 +- include/linux/nfs_xdr.h | 11 ++++++----- 3 files changed, 10 insertions(+), 9 deletions(-) (limited to 'include/linux') diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 54767dd66cf9..281c2def2b19 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -79,6 +79,7 @@ static int _nfs4_proc_open(struct nfs4_opendata *data); static int _nfs4_recover_proc_open(struct nfs4_opendata *data); static int nfs4_do_fsinfo(struct nfs_server *, struct nfs_fh *, struct nfs_fsinfo *); static int nfs4_async_handle_error(struct rpc_task *, const struct nfs_server *, struct nfs4_state *); +static void nfs_fixup_referral_attributes(struct nfs_fattr *fattr); static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr); static int nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred, struct nfs_fattr *fattr, struct iattr *sattr, @@ -2340,7 +2341,6 @@ static int nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle, return nfs4_map_errors(status); } -static void nfs_fixup_referral_attributes(struct nfs_fattr *fattr); /* * Get locations and (maybe) other attributes of a referral. * Note that we'll actually follow the referral later when @@ -4797,11 +4797,11 @@ static void nfs_fixup_referral_attributes(struct nfs_fattr *fattr) if (!(((fattr->valid & NFS_ATTR_FATTR_MOUNTED_ON_FILEID) || (fattr->valid & NFS_ATTR_FATTR_FILEID)) && (fattr->valid & NFS_ATTR_FATTR_FSID) && - (fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL))) + (fattr->valid & NFS_ATTR_FATTR_V4_LOCATIONS))) return; fattr->valid |= NFS_ATTR_FATTR_TYPE | NFS_ATTR_FATTR_MODE | - NFS_ATTR_FATTR_NLINK; + NFS_ATTR_FATTR_NLINK | NFS_ATTR_FATTR_V4_REFERRAL; fattr->mode = S_IFDIR | S_IRUGO | S_IXUGO; fattr->nlink = 2; } diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 48f539314f25..a6fb55da874c 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -3660,7 +3660,7 @@ static int decode_attr_fs_locations(struct xdr_stream *xdr, uint32_t *bitmap, st res->nlocations++; } if (res->nlocations != 0) - status = NFS_ATTR_FATTR_V4_REFERRAL; + status = NFS_ATTR_FATTR_V4_LOCATIONS; out: dprintk("%s: fs_locations done, error = %d\n", __func__, status); return status; diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 046c1bfddc33..210da5dc4f17 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -88,11 +88,12 @@ struct nfs_fattr { #define NFS_ATTR_FATTR_PRECTIME (1U << 16) #define NFS_ATTR_FATTR_CHANGE (1U << 17) #define NFS_ATTR_FATTR_PRECHANGE (1U << 18) -#define NFS_ATTR_FATTR_V4_REFERRAL (1U << 19) /* NFSv4 referral */ -#define NFS_ATTR_FATTR_MOUNTPOINT (1U << 20) /* Treat as mountpoint */ -#define NFS_ATTR_FATTR_MOUNTED_ON_FILEID (1U << 21) -#define NFS_ATTR_FATTR_OWNER_NAME (1U << 22) -#define NFS_ATTR_FATTR_GROUP_NAME (1U << 23) +#define NFS_ATTR_FATTR_V4_LOCATIONS (1U << 19) +#define NFS_ATTR_FATTR_V4_REFERRAL (1U << 20) +#define NFS_ATTR_FATTR_MOUNTPOINT (1U << 21) +#define NFS_ATTR_FATTR_MOUNTED_ON_FILEID (1U << 22) +#define NFS_ATTR_FATTR_OWNER_NAME (1U << 23) +#define NFS_ATTR_FATTR_GROUP_NAME (1U << 24) #define NFS_ATTR_FATTR (NFS_ATTR_FATTR_TYPE \ | NFS_ATTR_FATTR_MODE \ -- cgit v1.2.3 From 264e6351c59d22303582c45d79f0a5735f51d8d1 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Thu, 1 Mar 2012 17:02:05 -0500 Subject: NFS: Request fh_expire_type attribute in "server caps" operation The fh_expire_type file attribute is a filesystem wide attribute that consists of flags that indicate what characteristics file handles on this FSID have. Our client doesn't support volatile file handles. It should find out early (say, at mount time) whether the server is going to play shenanighans with file handles during a migration. Signed-off-by: Chuck Lever Signed-off-by: Trond Myklebust --- fs/nfs/nfs4proc.c | 1 + fs/nfs/nfs4xdr.c | 26 ++++++++++++++++++++++++++ include/linux/nfs_fs_sb.h | 3 +++ include/linux/nfs_xdr.h | 1 + 4 files changed, 31 insertions(+) (limited to 'include/linux') diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 281c2def2b19..87b9b91f76cf 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -2220,6 +2220,7 @@ static int _nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *f server->cache_consistency_bitmask[0] &= FATTR4_WORD0_CHANGE|FATTR4_WORD0_SIZE; server->cache_consistency_bitmask[1] &= FATTR4_WORD1_TIME_METADATA|FATTR4_WORD1_TIME_MODIFY; server->acl_bitmask = res.acl_bitmask; + server->fh_expire_type = res.fh_expire_type; } return status; diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index a6fb55da874c..3e0fe9f92e7c 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -2676,6 +2676,7 @@ static void nfs4_xdr_enc_server_caps(struct rpc_rqst *req, encode_sequence(xdr, &args->seq_args, &hdr); encode_putfh(xdr, args->fhandle, &hdr); encode_getattr_one(xdr, FATTR4_WORD0_SUPPORTED_ATTRS| + FATTR4_WORD0_FH_EXPIRE_TYPE| FATTR4_WORD0_LINK_SUPPORT| FATTR4_WORD0_SYMLINK_SUPPORT| FATTR4_WORD0_ACLSUPPORT, &hdr); @@ -3223,6 +3224,28 @@ out_overflow: return -EIO; } +static int decode_attr_fh_expire_type(struct xdr_stream *xdr, + uint32_t *bitmap, uint32_t *type) +{ + __be32 *p; + + *type = 0; + if (unlikely(bitmap[0] & (FATTR4_WORD0_FH_EXPIRE_TYPE - 1U))) + return -EIO; + if (likely(bitmap[0] & FATTR4_WORD0_FH_EXPIRE_TYPE)) { + p = xdr_inline_decode(xdr, 4); + if (unlikely(!p)) + goto out_overflow; + *type = be32_to_cpup(p); + bitmap[0] &= ~FATTR4_WORD0_FH_EXPIRE_TYPE; + } + dprintk("%s: expire type=0x%x\n", __func__, *type); + return 0; +out_overflow: + print_overflow_msg(__func__, xdr); + return -EIO; +} + static int decode_attr_change(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *change) { __be32 *p; @@ -4271,6 +4294,9 @@ static int decode_server_caps(struct xdr_stream *xdr, struct nfs4_server_caps_re goto xdr_error; if ((status = decode_attr_supported(xdr, bitmap, res->attr_bitmask)) != 0) goto xdr_error; + if ((status = decode_attr_fh_expire_type(xdr, bitmap, + &res->fh_expire_type)) != 0) + goto xdr_error; if ((status = decode_attr_link_support(xdr, bitmap, &res->has_links)) != 0) goto xdr_error; if ((status = decode_attr_symlink_support(xdr, bitmap, &res->has_symlinks)) != 0) diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h index 03d0b91c2d5b..7073fc74481c 100644 --- a/include/linux/nfs_fs_sb.h +++ b/include/linux/nfs_fs_sb.h @@ -148,6 +148,9 @@ struct nfs_server { u32 acl_bitmask; /* V4 bitmask representing the ACEs that are supported on this filesystem */ + u32 fh_expire_type; /* V4 bitmask representing file + handle volatility type for + this filesystem */ struct pnfs_layoutdriver_type *pnfs_curr_ld; /* Active layout driver */ struct rpc_wait_queue roc_rpcwaitq; void *pnfs_ld_data; /* per mount point data */ diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 210da5dc4f17..6f4c35941965 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -977,6 +977,7 @@ struct nfs4_server_caps_res { u32 acl_bitmask; u32 has_links; u32 has_symlinks; + u32 fh_expire_type; struct nfs4_sequence_res seq_res; }; -- cgit v1.2.3 From 1b8be32e6914ed862a5ce460c0a0b418ba85d2b7 Mon Sep 17 00:00:00 2001 From: Rachna Patil Date: Sun, 4 Mar 2012 08:11:57 -0800 Subject: Input: add support for TI Touchscreen controller This patch adds support for TI's touchscreen controller for a 4/5/8 wire resistive panel that is directly fed to the ADC. This touchscreen controller will be part of AM335x TI SoC. The TRM can be found at: http://www.ti.com/lit/ug/spruh73a/spruh73a.pdf Signed-off-by: Patil, Rachna Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/Kconfig | 12 + drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/ti_tscadc.c | 486 ++++++++++++++++++++++++++++++++++ include/linux/input/ti_tscadc.h | 17 ++ 4 files changed, 516 insertions(+) create mode 100644 drivers/input/touchscreen/ti_tscadc.c create mode 100644 include/linux/input/ti_tscadc.h (limited to 'include/linux') diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 2b21a7066d36..5df719b591c3 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -460,6 +460,18 @@ config TOUCHSCREEN_TOUCHWIN To compile this driver as a module, choose M here: the module will be called touchwin. +config TOUCHSCREEN_TI_TSCADC + tristate "TI Touchscreen Interface" + depends on ARCH_OMAP2PLUS + help + Say Y here if you have 4/5/8 wire touchscreen controller + to be connected to the ADC controller on your TI AM335x SoC. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called ti_tscadc. + config TOUCHSCREEN_ATMEL_TSADCC tristate "Atmel Touchscreen Interface" depends on ARCH_AT91SAM9RL || ARCH_AT91SAM9G45 diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index d9acbdcda3d9..e748fb837759 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -48,6 +48,7 @@ obj-$(CONFIG_TOUCHSCREEN_PIXCIR) += pixcir_i2c_ts.o obj-$(CONFIG_TOUCHSCREEN_S3C2410) += s3c2410_ts.o obj-$(CONFIG_TOUCHSCREEN_ST1232) += st1232.o obj-$(CONFIG_TOUCHSCREEN_STMPE) += stmpe-ts.o +obj-$(CONFIG_TOUCHSCREEN_TI_TSCADC) += ti_tscadc.o obj-$(CONFIG_TOUCHSCREEN_TNETV107X) += tnetv107x-ts.o obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o diff --git a/drivers/input/touchscreen/ti_tscadc.c b/drivers/input/touchscreen/ti_tscadc.c new file mode 100644 index 000000000000..d229c741d544 --- /dev/null +++ b/drivers/input/touchscreen/ti_tscadc.c @@ -0,0 +1,486 @@ +/* + * TI Touch Screen driver + * + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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 version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define REG_IRQEOI 0x020 +#define REG_RAWIRQSTATUS 0x024 +#define REG_IRQSTATUS 0x028 +#define REG_IRQENABLE 0x02C +#define REG_IRQWAKEUP 0x034 +#define REG_CTRL 0x040 +#define REG_ADCFSM 0x044 +#define REG_CLKDIV 0x04C +#define REG_SE 0x054 +#define REG_IDLECONFIG 0x058 +#define REG_CHARGECONFIG 0x05C +#define REG_CHARGEDELAY 0x060 +#define REG_STEPCONFIG(n) (0x64 + ((n - 1) * 8)) +#define REG_STEPDELAY(n) (0x68 + ((n - 1) * 8)) +#define REG_STEPCONFIG13 0x0C4 +#define REG_STEPDELAY13 0x0C8 +#define REG_STEPCONFIG14 0x0CC +#define REG_STEPDELAY14 0x0D0 +#define REG_FIFO0CNT 0xE4 +#define REG_FIFO1THR 0xF4 +#define REG_FIFO0 0x100 +#define REG_FIFO1 0x200 + +/* Register Bitfields */ +#define IRQWKUP_ENB BIT(0) +#define STPENB_STEPENB 0x7FFF +#define IRQENB_FIFO1THRES BIT(5) +#define IRQENB_PENUP BIT(9) +#define STEPCONFIG_MODE_HWSYNC 0x2 +#define STEPCONFIG_SAMPLES_AVG (1 << 4) +#define STEPCONFIG_XPP (1 << 5) +#define STEPCONFIG_XNN (1 << 6) +#define STEPCONFIG_YPP (1 << 7) +#define STEPCONFIG_YNN (1 << 8) +#define STEPCONFIG_XNP (1 << 9) +#define STEPCONFIG_YPN (1 << 10) +#define STEPCONFIG_INM (1 << 18) +#define STEPCONFIG_INP (1 << 20) +#define STEPCONFIG_INP_5 (1 << 21) +#define STEPCONFIG_FIFO1 (1 << 26) +#define STEPCONFIG_OPENDLY 0xff +#define STEPCONFIG_Z1 (3 << 19) +#define STEPIDLE_INP (1 << 22) +#define STEPCHARGE_RFP (1 << 12) +#define STEPCHARGE_INM (1 << 15) +#define STEPCHARGE_INP (1 << 19) +#define STEPCHARGE_RFM (1 << 23) +#define STEPCHARGE_DELAY 0x1 +#define CNTRLREG_TSCSSENB (1 << 0) +#define CNTRLREG_STEPID (1 << 1) +#define CNTRLREG_STEPCONFIGWRT (1 << 2) +#define CNTRLREG_4WIRE (1 << 5) +#define CNTRLREG_5WIRE (1 << 6) +#define CNTRLREG_8WIRE (3 << 5) +#define CNTRLREG_TSCENB (1 << 7) +#define ADCFSM_STEPID 0x10 + +#define SEQ_SETTLE 275 +#define ADC_CLK 3000000 +#define MAX_12BIT ((1 << 12) - 1) +#define TSCADC_DELTA_X 15 +#define TSCADC_DELTA_Y 15 + +struct tscadc { + struct input_dev *input; + struct clk *tsc_ick; + void __iomem *tsc_base; + unsigned int irq; + unsigned int wires; + unsigned int x_plate_resistance; + bool pen_down; +}; + +static unsigned int tscadc_readl(struct tscadc *ts, unsigned int reg) +{ + return readl(ts->tsc_base + reg); +} + +static void tscadc_writel(struct tscadc *tsc, unsigned int reg, + unsigned int val) +{ + writel(val, tsc->tsc_base + reg); +} + +static void tscadc_step_config(struct tscadc *ts_dev) +{ + unsigned int config; + int i; + + /* Configure the Step registers */ + + config = STEPCONFIG_MODE_HWSYNC | + STEPCONFIG_SAMPLES_AVG | STEPCONFIG_XPP; + switch (ts_dev->wires) { + case 4: + config |= STEPCONFIG_INP | STEPCONFIG_XNN; + break; + case 5: + config |= STEPCONFIG_YNN | + STEPCONFIG_INP_5 | STEPCONFIG_XNN | + STEPCONFIG_YPP; + break; + case 8: + config |= STEPCONFIG_INP | STEPCONFIG_XNN; + break; + } + + for (i = 1; i < 7; i++) { + tscadc_writel(ts_dev, REG_STEPCONFIG(i), config); + tscadc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY); + } + + config = 0; + config = STEPCONFIG_MODE_HWSYNC | + STEPCONFIG_SAMPLES_AVG | STEPCONFIG_YNN | + STEPCONFIG_INM | STEPCONFIG_FIFO1; + switch (ts_dev->wires) { + case 4: + config |= STEPCONFIG_YPP; + break; + case 5: + config |= STEPCONFIG_XPP | STEPCONFIG_INP_5 | + STEPCONFIG_XNP | STEPCONFIG_YPN; + break; + case 8: + config |= STEPCONFIG_YPP; + break; + } + + for (i = 7; i < 13; i++) { + tscadc_writel(ts_dev, REG_STEPCONFIG(i), config); + tscadc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY); + } + + config = 0; + /* Charge step configuration */ + config = STEPCONFIG_XPP | STEPCONFIG_YNN | + STEPCHARGE_RFP | STEPCHARGE_RFM | + STEPCHARGE_INM | STEPCHARGE_INP; + + tscadc_writel(ts_dev, REG_CHARGECONFIG, config); + tscadc_writel(ts_dev, REG_CHARGEDELAY, STEPCHARGE_DELAY); + + config = 0; + /* Configure to calculate pressure */ + config = STEPCONFIG_MODE_HWSYNC | + STEPCONFIG_SAMPLES_AVG | STEPCONFIG_YPP | + STEPCONFIG_XNN | STEPCONFIG_INM; + tscadc_writel(ts_dev, REG_STEPCONFIG13, config); + tscadc_writel(ts_dev, REG_STEPDELAY13, STEPCONFIG_OPENDLY); + + config |= STEPCONFIG_Z1 | STEPCONFIG_FIFO1; + tscadc_writel(ts_dev, REG_STEPCONFIG14, config); + tscadc_writel(ts_dev, REG_STEPDELAY14, STEPCONFIG_OPENDLY); + + tscadc_writel(ts_dev, REG_SE, STPENB_STEPENB); +} + +static void tscadc_idle_config(struct tscadc *ts_config) +{ + unsigned int idleconfig; + + idleconfig = STEPCONFIG_YNN | + STEPCONFIG_INM | + STEPCONFIG_YPN | STEPIDLE_INP; + tscadc_writel(ts_config, REG_IDLECONFIG, idleconfig); +} + +static void tscadc_read_coordinates(struct tscadc *ts_dev, + unsigned int *x, unsigned int *y) +{ + unsigned int fifocount = tscadc_readl(ts_dev, REG_FIFO0CNT); + unsigned int prev_val_x = ~0, prev_val_y = ~0; + unsigned int prev_diff_x = ~0, prev_diff_y = ~0; + unsigned int read, diff; + unsigned int i; + + /* + * Delta filter is used to remove large variations in sampled + * values from ADC. The filter tries to predict where the next + * coordinate could be. This is done by taking a previous + * coordinate and subtracting it form current one. Further the + * algorithm compares the difference with that of a present value, + * if true the value is reported to the sub system. + */ + for (i = 0; i < fifocount - 1; i++) { + read = tscadc_readl(ts_dev, REG_FIFO0) & 0xfff; + diff = abs(read - prev_val_x); + if (diff < prev_diff_x) { + prev_diff_x = diff; + *x = read; + } + prev_val_x = read; + + read = tscadc_readl(ts_dev, REG_FIFO1) & 0xfff; + diff = abs(read - prev_val_y); + if (diff < prev_diff_y) { + prev_diff_y = diff; + *y = read; + } + prev_val_y = read; + } +} + +static irqreturn_t tscadc_irq(int irq, void *dev) +{ + struct tscadc *ts_dev = dev; + struct input_dev *input_dev = ts_dev->input; + unsigned int status, irqclr = 0; + unsigned int x = 0, y = 0; + unsigned int z1, z2, z; + unsigned int fsm; + + status = tscadc_readl(ts_dev, REG_IRQSTATUS); + if (status & IRQENB_FIFO1THRES) { + tscadc_read_coordinates(ts_dev, &x, &y); + + z1 = tscadc_readl(ts_dev, REG_FIFO0) & 0xfff; + z2 = tscadc_readl(ts_dev, REG_FIFO1) & 0xfff; + + if (ts_dev->pen_down && z1 != 0 && z2 != 0) { + /* + * Calculate pressure using formula + * Resistance(touch) = x plate resistance * + * x postion/4096 * ((z2 / z1) - 1) + */ + z = z2 - z1; + z *= x; + z *= ts_dev->x_plate_resistance; + z /= z1; + z = (z + 2047) >> 12; + + if (z <= MAX_12BIT) { + input_report_abs(input_dev, ABS_X, x); + input_report_abs(input_dev, ABS_Y, y); + input_report_abs(input_dev, ABS_PRESSURE, z); + input_report_key(input_dev, BTN_TOUCH, 1); + input_sync(input_dev); + } + } + irqclr |= IRQENB_FIFO1THRES; + } + + /* + * Time for sequencer to settle, to read + * correct state of the sequencer. + */ + udelay(SEQ_SETTLE); + + status = tscadc_readl(ts_dev, REG_RAWIRQSTATUS); + if (status & IRQENB_PENUP) { + /* Pen up event */ + fsm = tscadc_readl(ts_dev, REG_ADCFSM); + if (fsm == ADCFSM_STEPID) { + ts_dev->pen_down = false; + input_report_key(input_dev, BTN_TOUCH, 0); + input_report_abs(input_dev, ABS_PRESSURE, 0); + input_sync(input_dev); + } else { + ts_dev->pen_down = true; + } + irqclr |= IRQENB_PENUP; + } + + tscadc_writel(ts_dev, REG_IRQSTATUS, irqclr); + /* check pending interrupts */ + tscadc_writel(ts_dev, REG_IRQEOI, 0x0); + + tscadc_writel(ts_dev, REG_SE, STPENB_STEPENB); + return IRQ_HANDLED; +} + +/* + * The functions for inserting/removing driver as a module. + */ + +static int __devinit tscadc_probe(struct platform_device *pdev) +{ + const struct tsc_data *pdata = pdev->dev.platform_data; + struct resource *res; + struct tscadc *ts_dev; + struct input_dev *input_dev; + struct clk *clk; + int err; + int clk_value, ctrl, irq; + + if (!pdata) { + dev_err(&pdev->dev, "missing platform data.\n"); + return -EINVAL; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "no memory resource defined.\n"); + return -EINVAL; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "no irq ID is specified.\n"); + return -EINVAL; + } + + /* Allocate memory for device */ + ts_dev = kzalloc(sizeof(struct tscadc), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!ts_dev || !input_dev) { + dev_err(&pdev->dev, "failed to allocate memory.\n"); + err = -ENOMEM; + goto err_free_mem; + } + + ts_dev->input = input_dev; + ts_dev->irq = irq; + ts_dev->wires = pdata->wires; + ts_dev->x_plate_resistance = pdata->x_plate_resistance; + + res = request_mem_region(res->start, resource_size(res), pdev->name); + if (!res) { + dev_err(&pdev->dev, "failed to reserve registers.\n"); + err = -EBUSY; + goto err_free_mem; + } + + ts_dev->tsc_base = ioremap(res->start, resource_size(res)); + if (!ts_dev->tsc_base) { + dev_err(&pdev->dev, "failed to map registers.\n"); + err = -ENOMEM; + goto err_release_mem_region; + } + + err = request_irq(ts_dev->irq, tscadc_irq, + 0, pdev->dev.driver->name, ts_dev); + if (err) { + dev_err(&pdev->dev, "failed to allocate irq.\n"); + goto err_unmap_regs; + } + + ts_dev->tsc_ick = clk_get(&pdev->dev, "adc_tsc_ick"); + if (IS_ERR(ts_dev->tsc_ick)) { + dev_err(&pdev->dev, "failed to get TSC ick\n"); + goto err_free_irq; + } + clk_enable(ts_dev->tsc_ick); + + clk = clk_get(&pdev->dev, "adc_tsc_fck"); + if (IS_ERR(clk)) { + dev_err(&pdev->dev, "failed to get TSC fck\n"); + err = PTR_ERR(clk); + goto err_disable_clk; + } + + clk_value = clk_get_rate(clk) / ADC_CLK; + clk_put(clk); + + if (clk_value < 7) { + dev_err(&pdev->dev, "clock input less than min clock requirement\n"); + goto err_disable_clk; + } + /* CLKDIV needs to be configured to the value minus 1 */ + tscadc_writel(ts_dev, REG_CLKDIV, clk_value - 1); + + /* Enable wake-up of the SoC using touchscreen */ + tscadc_writel(ts_dev, REG_IRQWAKEUP, IRQWKUP_ENB); + + ctrl = CNTRLREG_STEPCONFIGWRT | + CNTRLREG_TSCENB | + CNTRLREG_STEPID; + switch (ts_dev->wires) { + case 4: + ctrl |= CNTRLREG_4WIRE; + break; + case 5: + ctrl |= CNTRLREG_5WIRE; + break; + case 8: + ctrl |= CNTRLREG_8WIRE; + break; + } + tscadc_writel(ts_dev, REG_CTRL, ctrl); + + tscadc_idle_config(ts_dev); + tscadc_writel(ts_dev, REG_IRQENABLE, IRQENB_FIFO1THRES); + tscadc_step_config(ts_dev); + tscadc_writel(ts_dev, REG_FIFO1THR, 6); + + ctrl |= CNTRLREG_TSCSSENB; + tscadc_writel(ts_dev, REG_CTRL, ctrl); + + input_dev->name = "ti-tsc-adc"; + input_dev->dev.parent = &pdev->dev; + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + + input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, 0, 0); + input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, 0, 0); + input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT, 0, 0); + + /* register to the input system */ + err = input_register_device(input_dev); + if (err) + goto err_disable_clk; + + platform_set_drvdata(pdev, ts_dev); + return 0; + +err_disable_clk: + clk_disable(ts_dev->tsc_ick); + clk_put(ts_dev->tsc_ick); +err_free_irq: + free_irq(ts_dev->irq, ts_dev); +err_unmap_regs: + iounmap(ts_dev->tsc_base); +err_release_mem_region: + release_mem_region(res->start, resource_size(res)); +err_free_mem: + input_free_device(input_dev); + kfree(ts_dev); + return err; +} + +static int __devexit tscadc_remove(struct platform_device *pdev) +{ + struct tscadc *ts_dev = platform_get_drvdata(pdev); + struct resource *res; + + free_irq(ts_dev->irq, ts_dev); + + input_unregister_device(ts_dev->input); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + iounmap(ts_dev->tsc_base); + release_mem_region(res->start, resource_size(res)); + + clk_disable(ts_dev->tsc_ick); + clk_put(ts_dev->tsc_ick); + + kfree(ts_dev); + + platform_set_drvdata(pdev, NULL); + return 0; +} + +static struct platform_driver ti_tsc_driver = { + .probe = tscadc_probe, + .remove = __devexit_p(tscadc_remove), + .driver = { + .name = "tsc", + .owner = THIS_MODULE, + }, +}; +module_platform_driver(ti_tsc_driver); + +MODULE_DESCRIPTION("TI touchscreen controller driver"); +MODULE_AUTHOR("Rachna Patil "); +MODULE_LICENSE("GPL"); diff --git a/include/linux/input/ti_tscadc.h b/include/linux/input/ti_tscadc.h new file mode 100644 index 000000000000..b10a527a92a4 --- /dev/null +++ b/include/linux/input/ti_tscadc.h @@ -0,0 +1,17 @@ +#ifndef __LINUX_TI_TSCADC_H +#define __LINUX_TI_TSCADC_H + +/** + * struct tsc_data Touchscreen wire configuration + * @wires: Wires refer to application modes + * i.e. 4/5/8 wire touchscreen support + * on the platform. + * @x_plate_resistance: X plate resistance. + */ + +struct tsc_data { + int wires; + int x_plate_resistance; +}; + +#endif -- cgit v1.2.3 From 187f1882b5b0748b3c4c22274663fdb372ac0452 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Wed, 23 Nov 2011 20:12:59 -0500 Subject: BUG: headers with BUG/BUG_ON etc. need linux/bug.h If a header file is making use of BUG, BUG_ON, BUILD_BUG_ON, or any other BUG variant in a static inline (i.e. not in a #define) then that header really should be including and not just expecting it to be implicitly present. We can make this change risk-free, since if the files using these headers didn't have exposure to linux/bug.h already, they would have been causing compile failures/warnings. Signed-off-by: Paul Gortmaker --- arch/avr32/include/asm/io.h | 1 + arch/m68k/include/asm/system.h | 1 + arch/sparc/include/asm/vga.h | 1 + arch/x86/include/asm/paravirt.h | 1 + include/asm-generic/dma-mapping-common.h | 1 + include/asm-generic/pgtable.h | 1 + include/asm-generic/tlbflush.h | 2 ++ include/drm/ttm/ttm_memory.h | 1 + include/linux/atmdev.h | 1 + include/linux/bio.h | 1 + include/linux/bit_spinlock.h | 1 + include/linux/ceph/decode.h | 3 ++- include/linux/ceph/libceph.h | 1 + include/linux/ceph/mdsmap.h | 1 + include/linux/cpumask.h | 1 + include/linux/crypto.h | 1 + include/linux/debug_locks.h | 1 + include/linux/dmaengine.h | 1 + include/linux/elfcore.h | 1 + include/linux/ext3_fs.h | 1 + include/linux/fs.h | 1 + include/linux/fsnotify.h | 1 + include/linux/gpio.h | 1 + include/linux/highmem.h | 1 + include/linux/i2o.h | 1 + include/linux/if_vlan.h | 1 + include/linux/io-mapping.h | 1 + include/linux/kprobes.h | 1 + include/linux/kvm_host.h | 1 + include/linux/memory_hotplug.h | 1 + include/linux/mm.h | 1 + include/linux/mtd/cfi.h | 1 + include/linux/netdevice.h | 1 + include/linux/nilfs2_fs.h | 1 + include/linux/page-flags.h | 1 + include/linux/pid_namespace.h | 1 + include/linux/posix_acl.h | 1 + include/linux/ptrace.h | 1 + include/linux/radix-tree.h | 1 + include/linux/rcupdate.h | 1 + include/linux/regset.h | 1 + include/linux/reiserfs_fs.h | 1 + include/linux/relay.h | 1 + include/linux/scatterlist.h | 6 ++++-- include/linux/seq_file.h | 1 + include/linux/skbuff.h | 1 + include/linux/slub_def.h | 1 + include/linux/ssb/ssb_driver_gige.h | 1 + include/linux/swapops.h | 1 + include/linux/syscalls.h | 1 + include/linux/transport_class.h | 1 + include/linux/virtio_config.h | 1 + include/net/cfg80211.h | 1 + include/net/dst.h | 1 + include/net/ip_vs.h | 1 + include/net/mac80211.h | 1 + include/net/netns/generic.h | 1 + include/net/red.h | 1 + include/net/tcp.h | 1 + include/net/timewait_sock.h | 1 + include/net/udp.h | 1 + include/net/wpan-phy.h | 1 + include/scsi/osd_ore.h | 1 + include/scsi/scsi_transport.h | 1 + 64 files changed, 69 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/arch/avr32/include/asm/io.h b/arch/avr32/include/asm/io.h index 22c97ef92201..cf60d0a9f176 100644 --- a/arch/avr32/include/asm/io.h +++ b/arch/avr32/include/asm/io.h @@ -1,6 +1,7 @@ #ifndef __ASM_AVR32_IO_H #define __ASM_AVR32_IO_H +#include #include #include #include diff --git a/arch/m68k/include/asm/system.h b/arch/m68k/include/asm/system.h index 47b01f4726bc..8dc68178716c 100644 --- a/arch/m68k/include/asm/system.h +++ b/arch/m68k/include/asm/system.h @@ -3,6 +3,7 @@ #include #include +#include #include #include #include diff --git a/arch/sparc/include/asm/vga.h b/arch/sparc/include/asm/vga.h index c69d5b2ba19a..ec0e9967d93d 100644 --- a/arch/sparc/include/asm/vga.h +++ b/arch/sparc/include/asm/vga.h @@ -7,6 +7,7 @@ #ifndef _LINUX_ASM_VGA_H_ #define _LINUX_ASM_VGA_H_ +#include #include #define VT_BUF_HAVE_RW diff --git a/arch/x86/include/asm/paravirt.h b/arch/x86/include/asm/paravirt.h index a7d2db9a74fb..923b07024a03 100644 --- a/arch/x86/include/asm/paravirt.h +++ b/arch/x86/include/asm/paravirt.h @@ -10,6 +10,7 @@ #include #ifndef __ASSEMBLY__ +#include #include #include diff --git a/include/asm-generic/dma-mapping-common.h b/include/asm-generic/dma-mapping-common.h index 9fa3f96e38cf..2e248d8924dc 100644 --- a/include/asm-generic/dma-mapping-common.h +++ b/include/asm-generic/dma-mapping-common.h @@ -2,6 +2,7 @@ #define _ASM_GENERIC_DMA_MAPPING_H #include +#include #include #include #include diff --git a/include/asm-generic/pgtable.h b/include/asm-generic/pgtable.h index 76bff2bff15e..236b1056839f 100644 --- a/include/asm-generic/pgtable.h +++ b/include/asm-generic/pgtable.h @@ -5,6 +5,7 @@ #ifdef CONFIG_MMU #include +#include #ifndef __HAVE_ARCH_PTEP_SET_ACCESS_FLAGS extern int ptep_set_access_flags(struct vm_area_struct *vma, diff --git a/include/asm-generic/tlbflush.h b/include/asm-generic/tlbflush.h index c7af037024c7..d6d0a88430fe 100644 --- a/include/asm-generic/tlbflush.h +++ b/include/asm-generic/tlbflush.h @@ -9,6 +9,8 @@ #error need to implement an architecture specific asm/tlbflush.h #endif +#include + static inline void flush_tlb_mm(struct mm_struct *mm) { BUG(); diff --git a/include/drm/ttm/ttm_memory.h b/include/drm/ttm/ttm_memory.h index 26c1f78d136f..d6d1da468c97 100644 --- a/include/drm/ttm/ttm_memory.h +++ b/include/drm/ttm/ttm_memory.h @@ -30,6 +30,7 @@ #include #include +#include #include #include #include diff --git a/include/linux/atmdev.h b/include/linux/atmdev.h index f4ff882cb2da..42c471afc52a 100644 --- a/include/linux/atmdev.h +++ b/include/linux/atmdev.h @@ -217,6 +217,7 @@ struct atm_cirange { #include /* wait_queue_head_t */ #include /* struct timeval */ #include +#include #include /* struct sk_buff */ #include #include diff --git a/include/linux/bio.h b/include/linux/bio.h index 129a9c097958..f54db088f335 100644 --- a/include/linux/bio.h +++ b/include/linux/bio.h @@ -23,6 +23,7 @@ #include #include #include +#include #ifdef CONFIG_BLOCK diff --git a/include/linux/bit_spinlock.h b/include/linux/bit_spinlock.h index ac4d9f8b52e9..3b5bafce4337 100644 --- a/include/linux/bit_spinlock.h +++ b/include/linux/bit_spinlock.h @@ -4,6 +4,7 @@ #include #include #include +#include /* * bit-based spin_lock() diff --git a/include/linux/ceph/decode.h b/include/linux/ceph/decode.h index c5b6939fb32a..220ae21e819b 100644 --- a/include/linux/ceph/decode.h +++ b/include/linux/ceph/decode.h @@ -1,8 +1,9 @@ #ifndef __CEPH_DECODE_H #define __CEPH_DECODE_H -#include +#include #include +#include #include "types.h" diff --git a/include/linux/ceph/libceph.h b/include/linux/ceph/libceph.h index 95bd8502e715..e8cf0ccd1a8d 100644 --- a/include/linux/ceph/libceph.h +++ b/include/linux/ceph/libceph.h @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include diff --git a/include/linux/ceph/mdsmap.h b/include/linux/ceph/mdsmap.h index 4c5cb0880bba..9935fac8c107 100644 --- a/include/linux/ceph/mdsmap.h +++ b/include/linux/ceph/mdsmap.h @@ -1,6 +1,7 @@ #ifndef _FS_CEPH_MDSMAP_H #define _FS_CEPH_MDSMAP_H +#include #include "types.h" /* diff --git a/include/linux/cpumask.h b/include/linux/cpumask.h index 4f7a63237471..7b9b75a529be 100644 --- a/include/linux/cpumask.h +++ b/include/linux/cpumask.h @@ -9,6 +9,7 @@ #include #include #include +#include typedef struct cpumask { DECLARE_BITMAP(bits, NR_CPUS); } cpumask_t; diff --git a/include/linux/crypto.h b/include/linux/crypto.h index 8a94217b298e..d870bae81df1 100644 --- a/include/linux/crypto.h +++ b/include/linux/crypto.h @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include diff --git a/include/linux/debug_locks.h b/include/linux/debug_locks.h index 5033fb88c107..94f20c1488a1 100644 --- a/include/linux/debug_locks.h +++ b/include/linux/debug_locks.h @@ -3,6 +3,7 @@ #include #include +#include #include struct task_struct; diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 679b349d9b66..a5966f691ef8 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -23,6 +23,7 @@ #include #include +#include #include #include #include diff --git a/include/linux/elfcore.h b/include/linux/elfcore.h index 394a3e0e4a6b..0698c79fbcb2 100644 --- a/include/linux/elfcore.h +++ b/include/linux/elfcore.h @@ -6,6 +6,7 @@ #include #ifdef __KERNEL__ #include +#include #endif #include #include diff --git a/include/linux/ext3_fs.h b/include/linux/ext3_fs.h index f957085d40ed..f5a84eef6ed2 100644 --- a/include/linux/ext3_fs.h +++ b/include/linux/ext3_fs.h @@ -18,6 +18,7 @@ #include #include +#include /* * The second extended filesystem constants/structures diff --git a/include/linux/fs.h b/include/linux/fs.h index 69cd5bb640f5..abc92db51e54 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -389,6 +389,7 @@ struct inodes_stat_t { #include #include #include +#include #include #include #include diff --git a/include/linux/fsnotify.h b/include/linux/fsnotify.h index 2a53f10712b3..a6dfe6944564 100644 --- a/include/linux/fsnotify.h +++ b/include/linux/fsnotify.h @@ -14,6 +14,7 @@ #include #include #include +#include /* * fsnotify_d_instantiate - instantiate a dentry for inode diff --git a/include/linux/gpio.h b/include/linux/gpio.h index 38ac48b7d3a8..ed5a46707ad0 100644 --- a/include/linux/gpio.h +++ b/include/linux/gpio.h @@ -34,6 +34,7 @@ struct gpio { #include #include #include +#include struct device; struct gpio_chip; diff --git a/include/linux/highmem.h b/include/linux/highmem.h index 3a93f73a8acc..6ede661e5b8e 100644 --- a/include/linux/highmem.h +++ b/include/linux/highmem.h @@ -3,6 +3,7 @@ #include #include +#include #include #include #include diff --git a/include/linux/i2o.h b/include/linux/i2o.h index a6deef4f4f67..d23c3c20b201 100644 --- a/include/linux/i2o.h +++ b/include/linux/i2o.h @@ -24,6 +24,7 @@ #define I2O_MAX_DRIVERS 8 #include +#include #include #include #include diff --git a/include/linux/if_vlan.h b/include/linux/if_vlan.h index 13aff1e2183b..82097f39df10 100644 --- a/include/linux/if_vlan.h +++ b/include/linux/if_vlan.h @@ -17,6 +17,7 @@ #include #include #include +#include #define VLAN_HLEN 4 /* The additional bytes (on top of the Ethernet header) * that VLAN requires. diff --git a/include/linux/io-mapping.h b/include/linux/io-mapping.h index e44e84f0156c..657fab4efab3 100644 --- a/include/linux/io-mapping.h +++ b/include/linux/io-mapping.h @@ -20,6 +20,7 @@ #include #include +#include #include #include diff --git a/include/linux/kprobes.h b/include/linux/kprobes.h index dce6e4dbeda7..b6e1f8c00577 100644 --- a/include/linux/kprobes.h +++ b/include/linux/kprobes.h @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index 900c76337e8f..ca1b153585d3 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include diff --git a/include/linux/memory_hotplug.h b/include/linux/memory_hotplug.h index 0b8e2a742600..910550f3b70e 100644 --- a/include/linux/memory_hotplug.h +++ b/include/linux/memory_hotplug.h @@ -4,6 +4,7 @@ #include #include #include +#include struct page; struct zone; diff --git a/include/linux/mm.h b/include/linux/mm.h index 17b27cd269c4..b7fac5b6acb6 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -6,6 +6,7 @@ #ifdef __KERNEL__ #include +#include #include #include #include diff --git a/include/linux/mtd/cfi.h b/include/linux/mtd/cfi.h index d5d2ec6494bb..37ef6b194089 100644 --- a/include/linux/mtd/cfi.h +++ b/include/linux/mtd/cfi.h @@ -22,6 +22,7 @@ #include #include +#include #include #include #include diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 0eac07c95255..5820638193f5 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -33,6 +33,7 @@ #ifdef __KERNEL__ #include #include +#include #include #include #include diff --git a/include/linux/nilfs2_fs.h b/include/linux/nilfs2_fs.h index 7454ad7451b4..89bd4a4dcfb4 100644 --- a/include/linux/nilfs2_fs.h +++ b/include/linux/nilfs2_fs.h @@ -41,6 +41,7 @@ #include #include #include +#include #define NILFS_INODE_BMAP_SIZE 7 diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h index e90a673be67e..3cfa3ad94b1f 100644 --- a/include/linux/page-flags.h +++ b/include/linux/page-flags.h @@ -6,6 +6,7 @@ #define PAGE_FLAGS_H #include +#include #ifndef __GENERATING_BOUNDS_H #include #include diff --git a/include/linux/pid_namespace.h b/include/linux/pid_namespace.h index e7cf6669ac34..f5bd679be46b 100644 --- a/include/linux/pid_namespace.h +++ b/include/linux/pid_namespace.h @@ -2,6 +2,7 @@ #define _LINUX_PID_NS_H #include +#include #include #include #include diff --git a/include/linux/posix_acl.h b/include/linux/posix_acl.h index b7681102a4b9..11bad91c4433 100644 --- a/include/linux/posix_acl.h +++ b/include/linux/posix_acl.h @@ -8,6 +8,7 @@ #ifndef __LINUX_POSIX_ACL_H #define __LINUX_POSIX_ACL_H +#include #include #include diff --git a/include/linux/ptrace.h b/include/linux/ptrace.h index c2f1f6a5fcb8..753ee8b62335 100644 --- a/include/linux/ptrace.h +++ b/include/linux/ptrace.h @@ -113,6 +113,7 @@ #include /* For unlikely. */ #include /* For struct task_struct. */ #include /* for IS_ERR_VALUE */ +#include /* For BUG_ON. */ extern long arch_ptrace(struct task_struct *child, long request, diff --git a/include/linux/radix-tree.h b/include/linux/radix-tree.h index 07e360b1b282..e9a48234e693 100644 --- a/include/linux/radix-tree.h +++ b/include/linux/radix-tree.h @@ -22,6 +22,7 @@ #include #include +#include #include #include diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h index 81c04f4348ec..3b657f2bed4a 100644 --- a/include/linux/rcupdate.h +++ b/include/linux/rcupdate.h @@ -42,6 +42,7 @@ #include #include #include +#include #include #ifdef CONFIG_RCU_TORTURE_TEST diff --git a/include/linux/regset.h b/include/linux/regset.h index 8abee6556223..6325e099105a 100644 --- a/include/linux/regset.h +++ b/include/linux/regset.h @@ -15,6 +15,7 @@ #include #include +#include #include struct task_struct; struct user_regset; diff --git a/include/linux/reiserfs_fs.h b/include/linux/reiserfs_fs.h index 2213ddcce20c..6643fb031293 100644 --- a/include/linux/reiserfs_fs.h +++ b/include/linux/reiserfs_fs.h @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include diff --git a/include/linux/relay.h b/include/linux/relay.h index a822fd71fd64..91cacc34c159 100644 --- a/include/linux/relay.h +++ b/include/linux/relay.h @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include diff --git a/include/linux/scatterlist.h b/include/linux/scatterlist.h index 9aaf5bfdad1a..ac9586dadfa5 100644 --- a/include/linux/scatterlist.h +++ b/include/linux/scatterlist.h @@ -1,10 +1,12 @@ #ifndef _LINUX_SCATTERLIST_H #define _LINUX_SCATTERLIST_H +#include +#include +#include + #include #include -#include -#include #include struct sg_table { diff --git a/include/linux/seq_file.h b/include/linux/seq_file.h index 44f1514b00ba..5ff2df6c8217 100644 --- a/include/linux/seq_file.h +++ b/include/linux/seq_file.h @@ -3,6 +3,7 @@ #include #include +#include #include #include #include diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 50db9b04a552..773ae985ec76 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -18,6 +18,7 @@ #include #include #include +#include #include #include diff --git a/include/linux/slub_def.h b/include/linux/slub_def.h index a32bcfdc7834..ca122b36aec1 100644 --- a/include/linux/slub_def.h +++ b/include/linux/slub_def.h @@ -8,6 +8,7 @@ */ #include #include +#include #include #include diff --git a/include/linux/ssb/ssb_driver_gige.h b/include/linux/ssb/ssb_driver_gige.h index eba52a100533..6b05dcd927ff 100644 --- a/include/linux/ssb/ssb_driver_gige.h +++ b/include/linux/ssb/ssb_driver_gige.h @@ -2,6 +2,7 @@ #define LINUX_SSB_DRIVER_GIGE_H_ #include +#include #include #include diff --git a/include/linux/swapops.h b/include/linux/swapops.h index 2189d3ffc85d..792d16d9cbc7 100644 --- a/include/linux/swapops.h +++ b/include/linux/swapops.h @@ -2,6 +2,7 @@ #define _LINUX_SWAPOPS_H #include +#include /* * swapcache pages are stored in the swapper_space radix tree. We want to diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 8ec1153ff57b..3de3acb84a95 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -68,6 +68,7 @@ struct file_handle; #include #include #include +#include #include #include #include diff --git a/include/linux/transport_class.h b/include/linux/transport_class.h index 9ae8da3e6407..11087cdd4ad3 100644 --- a/include/linux/transport_class.h +++ b/include/linux/transport_class.h @@ -10,6 +10,7 @@ #define _TRANSPORT_CLASS_H_ #include +#include #include struct transport_container; diff --git a/include/linux/virtio_config.h b/include/linux/virtio_config.h index 5206d6541da5..7323a3390206 100644 --- a/include/linux/virtio_config.h +++ b/include/linux/virtio_config.h @@ -53,6 +53,7 @@ #ifdef __KERNEL__ #include +#include #include /** diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index a067d30ce73e..85b44ca54ac6 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include diff --git a/include/net/dst.h b/include/net/dst.h index 344c8dd02874..59c5d18cc385 100644 --- a/include/net/dst.h +++ b/include/net/dst.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index ebe517f2da9f..2bdee51ba30d 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -16,6 +16,7 @@ #include /* for struct atomic_t */ #include #include +#include #include #include /* for union nf_inet_addr */ diff --git a/include/net/mac80211.h b/include/net/mac80211.h index d49928ba5d09..8294f44c425a 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -13,6 +13,7 @@ #ifndef MAC80211_H #define MAC80211_H +#include #include #include #include diff --git a/include/net/netns/generic.h b/include/net/netns/generic.h index d55f43443335..0931618c0f7f 100644 --- a/include/net/netns/generic.h +++ b/include/net/netns/generic.h @@ -5,6 +5,7 @@ #ifndef __NET_GENERIC_H__ #define __NET_GENERIC_H__ +#include #include /* diff --git a/include/net/red.h b/include/net/red.h index 28068ec614b2..77d4c3745cb5 100644 --- a/include/net/red.h +++ b/include/net/red.h @@ -2,6 +2,7 @@ #define __NET_SCHED_RED_H #include +#include #include #include #include diff --git a/include/net/tcp.h b/include/net/tcp.h index 42c29bfbcee3..ad8d0a865551 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -22,6 +22,7 @@ #include #include +#include #include #include #include diff --git a/include/net/timewait_sock.h b/include/net/timewait_sock.h index 053b3cf2c66a..8d6689cb2c66 100644 --- a/include/net/timewait_sock.h +++ b/include/net/timewait_sock.h @@ -12,6 +12,7 @@ #define _TIMEWAIT_SOCK_H #include +#include #include struct timewait_sock_ops { diff --git a/include/net/udp.h b/include/net/udp.h index e39592f682c3..5d606d9da9e5 100644 --- a/include/net/udp.h +++ b/include/net/udp.h @@ -23,6 +23,7 @@ #define _UDP_H #include +#include #include #include #include diff --git a/include/net/wpan-phy.h b/include/net/wpan-phy.h index d86fffd3c03c..ff27f1b078d1 100644 --- a/include/net/wpan-phy.h +++ b/include/net/wpan-phy.h @@ -23,6 +23,7 @@ #include #include +#include struct wpan_phy { struct mutex pib_lock; diff --git a/include/scsi/osd_ore.h b/include/scsi/osd_ore.h index f05fa826f89e..a5f9b960dfc8 100644 --- a/include/scsi/osd_ore.h +++ b/include/scsi/osd_ore.h @@ -26,6 +26,7 @@ #include #include #include +#include struct ore_comp { struct osd_obj_id obj; diff --git a/include/scsi/scsi_transport.h b/include/scsi/scsi_transport.h index 0de32cd4e8a7..af244f4bba53 100644 --- a/include/scsi/scsi_transport.h +++ b/include/scsi/scsi_transport.h @@ -22,6 +22,7 @@ #include #include +#include #include #include -- cgit v1.2.3 From 35edd9103c84f2b37f63227d12765c38f30495c5 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Wed, 16 Nov 2011 23:51:05 -0500 Subject: bug: consolidate BUILD_BUG_ON with other bug code The support for BUILD_BUG in linux/kernel.h predates the addition of linux/bug.h -- with this chunk off separate, you can run into situations where a person gets a compile fail even when they've included linux/bug.h, like this: CC lib/string.o lib/string.c: In function 'strlcat': lib/string.c:225:2: error: implicit declaration of function 'BUILD_BUG_ON' make[2]: *** [lib/string.o] Error 1 $ $ grep linux/bug.h lib/string.c #include $ Since the above violates the principle of least surprise, move the BUG chunks from kernel.h to bug.h so it is all together. Signed-off-by: Paul Gortmaker --- include/linux/bug.h | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/kernel.h | 61 -------------------------------------------------- 2 files changed, 61 insertions(+), 61 deletions(-) (limited to 'include/linux') diff --git a/include/linux/bug.h b/include/linux/bug.h index d276b5510c83..72961c39576a 100644 --- a/include/linux/bug.h +++ b/include/linux/bug.h @@ -11,6 +11,67 @@ enum bug_trap_type { struct pt_regs; +#ifdef __CHECKER__ +#define BUILD_BUG_ON_NOT_POWER_OF_2(n) +#define BUILD_BUG_ON_ZERO(e) (0) +#define BUILD_BUG_ON_NULL(e) ((void*)0) +#define BUILD_BUG_ON(condition) +#define BUILD_BUG() (0) +#else /* __CHECKER__ */ + +/* Force a compilation error if a constant expression is not a power of 2 */ +#define BUILD_BUG_ON_NOT_POWER_OF_2(n) \ + BUILD_BUG_ON((n) == 0 || (((n) & ((n) - 1)) != 0)) + +/* Force a compilation error if condition is true, but also produce a + result (of value 0 and type size_t), so the expression can be used + e.g. in a structure initializer (or where-ever else comma expressions + aren't permitted). */ +#define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); })) +#define BUILD_BUG_ON_NULL(e) ((void *)sizeof(struct { int:-!!(e); })) + +/** + * BUILD_BUG_ON - break compile if a condition is true. + * @condition: the condition which the compiler should know is false. + * + * If you have some code which relies on certain constants being equal, or + * other compile-time-evaluated condition, you should use BUILD_BUG_ON to + * detect if someone changes it. + * + * The implementation uses gcc's reluctance to create a negative array, but + * gcc (as of 4.4) only emits that error for obvious cases (eg. not arguments + * to inline functions). So as a fallback we use the optimizer; if it can't + * prove the condition is false, it will cause a link error on the undefined + * "__build_bug_on_failed". This error message can be harder to track down + * though, hence the two different methods. + */ +#ifndef __OPTIMIZE__ +#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)])) +#else +extern int __build_bug_on_failed; +#define BUILD_BUG_ON(condition) \ + do { \ + ((void)sizeof(char[1 - 2*!!(condition)])); \ + if (condition) __build_bug_on_failed = 1; \ + } while(0) +#endif + +/** + * BUILD_BUG - break compile if used. + * + * If you have some code that you expect the compiler to eliminate at + * build time, you should use BUILD_BUG to detect if it is + * unexpectedly used. + */ +#define BUILD_BUG() \ + do { \ + extern void __build_bug_failed(void) \ + __linktime_error("BUILD_BUG failed"); \ + __build_bug_failed(); \ + } while (0) + +#endif /* __CHECKER__ */ + #ifdef CONFIG_GENERIC_BUG #include diff --git a/include/linux/kernel.h b/include/linux/kernel.h index e8343422240a..5dba983b8d65 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -662,67 +662,6 @@ static inline void ftrace_dump(enum ftrace_dump_mode oops_dump_mode) { } const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );}) -#ifdef __CHECKER__ -#define BUILD_BUG_ON_NOT_POWER_OF_2(n) -#define BUILD_BUG_ON_ZERO(e) (0) -#define BUILD_BUG_ON_NULL(e) ((void*)0) -#define BUILD_BUG_ON(condition) -#define BUILD_BUG() (0) -#else /* __CHECKER__ */ - -/* Force a compilation error if a constant expression is not a power of 2 */ -#define BUILD_BUG_ON_NOT_POWER_OF_2(n) \ - BUILD_BUG_ON((n) == 0 || (((n) & ((n) - 1)) != 0)) - -/* Force a compilation error if condition is true, but also produce a - result (of value 0 and type size_t), so the expression can be used - e.g. in a structure initializer (or where-ever else comma expressions - aren't permitted). */ -#define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); })) -#define BUILD_BUG_ON_NULL(e) ((void *)sizeof(struct { int:-!!(e); })) - -/** - * BUILD_BUG_ON - break compile if a condition is true. - * @condition: the condition which the compiler should know is false. - * - * If you have some code which relies on certain constants being equal, or - * other compile-time-evaluated condition, you should use BUILD_BUG_ON to - * detect if someone changes it. - * - * The implementation uses gcc's reluctance to create a negative array, but - * gcc (as of 4.4) only emits that error for obvious cases (eg. not arguments - * to inline functions). So as a fallback we use the optimizer; if it can't - * prove the condition is false, it will cause a link error on the undefined - * "__build_bug_on_failed". This error message can be harder to track down - * though, hence the two different methods. - */ -#ifndef __OPTIMIZE__ -#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)])) -#else -extern int __build_bug_on_failed; -#define BUILD_BUG_ON(condition) \ - do { \ - ((void)sizeof(char[1 - 2*!!(condition)])); \ - if (condition) __build_bug_on_failed = 1; \ - } while(0) -#endif - -/** - * BUILD_BUG - break compile if used. - * - * If you have some code that you expect the compiler to eliminate at - * build time, you should use BUILD_BUG to detect if it is - * unexpectedly used. - */ -#define BUILD_BUG() \ - do { \ - extern void __build_bug_failed(void) \ - __linktime_error("BUILD_BUG failed"); \ - __build_bug_failed(); \ - } while (0) - -#endif /* __CHECKER__ */ - /* Trap pasters of __FUNCTION__ at compile-time */ #define __FUNCTION__ (__func__) -- cgit v1.2.3 From 6c03438edeb5c359af35f060ea016ca65671c269 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Wed, 23 Nov 2011 21:39:49 -0500 Subject: kernel.h: doesn't explicitly use bug.h, so don't include it. This header isn't using bug.h infrastructure, but due to historical reasons, it was including it. Removing it revealed several implicit dependencies (since kernel.h is everywhere) so we've fixed those 1st before deploying this change. Signed-off-by: Paul Gortmaker --- include/linux/kernel.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/kernel.h b/include/linux/kernel.h index 5dba983b8d65..bef5480396d0 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -20,7 +20,6 @@ #include #include #include -#include #define USHRT_MAX ((u16)(~0U)) #define SHRT_MAX ((s16)(USHRT_MAX>>1)) -- cgit v1.2.3 From 57b676f9c1b7cd84397fe5a86c9bd2788ac4bd32 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Fri, 2 Mar 2012 13:05:44 -0700 Subject: pinctrl: fix and simplify locking There are many problems with the current pinctrl locking: struct pinctrl_dev's gpio_ranges_lock isn't effective; pinctrl_match_gpio_range() only holds this lock while searching for a gpio range, but the found range is return and manipulated after releading the lock. This could allow pinctrl_remove_gpio_range() for that range while it is in use, and the caller may very well delete the range after removing it, causing pinctrl code to touch the now-free range object. Solving this requires the introduction of a higher-level lock, at least a lock per pin controller, which both gpio range registration and pinctrl_get()/put() will acquire. There is missing locking on HW programming; pin controllers may pack the configuration for different pins/groups/config options/... into one register, and hence have to read-modify-write the register. This needs to be protected, but currently isn't. Related, a future change will add a "complete" op to the pin controller drivers, the idea being that each state's programming will be programmed into the pinctrl driver followed by the "complete" call, which may e.g. flush a register cache to HW. For this to work, it must not be possible to interleave the pinctrl driver calls for different devices. As above, solving this requires the introduction of a higher-level lock, at least a lock per pin controller, which will be held for the duration of any pinctrl_enable()/disable() call. However, each pinctrl mapping table entry may affect a different pin controller if necessary. Hence, with a per-pin-controller lock, almost any pinctrl API may need to acquire multiple locks, one per controller. To avoid deadlock, these would need to be acquired in the same order in all cases. This is extremely difficult to implement in the case of pinctrl_get(), which doesn't know which pin controllers to lock until it has parsed the entire mapping table, since it contains somewhat arbitrary data. The simplest solution here is to introduce a single lock that covers all pin controllers at once. This will be acquired by all pinctrl APIs. This then makes struct pinctrl's mutex irrelevant, since that single lock will always be held whenever this mutex is currently held. Signed-off-by: Stephen Warren Signed-off-by: Linus Walleij --- drivers/pinctrl/core.c | 198 +++++++++++++++++++++++++--------------- drivers/pinctrl/core.h | 10 +- drivers/pinctrl/pinconf.c | 107 ++++++++++++++++------ drivers/pinctrl/pinmux.c | 21 ++--- include/linux/pinctrl/pinctrl.h | 1 - 5 files changed, 215 insertions(+), 122 deletions(-) (limited to 'include/linux') diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c index 6af6d8d117df..aefc3394db91 100644 --- a/drivers/pinctrl/core.c +++ b/drivers/pinctrl/core.c @@ -18,11 +18,8 @@ #include #include #include -#include #include #include -#include -#include #include #include #include @@ -44,16 +41,16 @@ struct pinctrl_maps { unsigned num_maps; }; -/* Global list of pin control devices */ -static DEFINE_MUTEX(pinctrldev_list_mutex); +/* Mutex taken by all entry points */ +DEFINE_MUTEX(pinctrl_mutex); + +/* Global list of pin control devices (struct pinctrl_dev) */ static LIST_HEAD(pinctrldev_list); -/* List of pin controller handles */ -static DEFINE_MUTEX(pinctrl_list_mutex); +/* List of pin controller handles (struct pinctrl) */ static LIST_HEAD(pinctrl_list); -/* Global pinctrl maps */ -static DEFINE_MUTEX(pinctrl_maps_mutex); +/* List of pinctrl maps (struct pinctrl_maps) */ static LIST_HEAD(pinctrl_maps); #define for_each_maps(_maps_node_, _i_, _map_) \ @@ -90,7 +87,6 @@ struct pinctrl_dev *get_pinctrl_dev_from_devname(const char *devname) if (!devname) return NULL; - mutex_lock(&pinctrldev_list_mutex); list_for_each_entry(pctldev, &pinctrldev_list, node) { if (!strcmp(dev_name(pctldev->dev), devname)) { /* Matched on device name */ @@ -98,7 +94,6 @@ struct pinctrl_dev *get_pinctrl_dev_from_devname(const char *devname) break; } } - mutex_unlock(&pinctrldev_list_mutex); return found ? pctldev : NULL; } @@ -143,11 +138,11 @@ bool pin_is_valid(struct pinctrl_dev *pctldev, int pin) if (pin < 0) return false; + mutex_lock(&pinctrl_mutex); pindesc = pin_desc_get(pctldev, pin); - if (pindesc == NULL) - return false; + mutex_unlock(&pinctrl_mutex); - return true; + return pindesc != NULL; } EXPORT_SYMBOL_GPL(pin_is_valid); @@ -191,8 +186,6 @@ static int pinctrl_register_one_pin(struct pinctrl_dev *pctldev, return -ENOMEM; } - spin_lock_init(&pindesc->lock); - /* Set owner */ pindesc->pctldev = pctldev; @@ -243,16 +236,13 @@ pinctrl_match_gpio_range(struct pinctrl_dev *pctldev, unsigned gpio) struct pinctrl_gpio_range *range = NULL; /* Loop over the ranges */ - mutex_lock(&pctldev->gpio_ranges_lock); list_for_each_entry(range, &pctldev->gpio_ranges, node) { /* Check if we're in the valid range */ if (gpio >= range->base && gpio < range->base + range->npins) { - mutex_unlock(&pctldev->gpio_ranges_lock); return range; } } - mutex_unlock(&pctldev->gpio_ranges_lock); return NULL; } @@ -274,7 +264,6 @@ static int pinctrl_get_device_gpio_range(unsigned gpio, struct pinctrl_dev *pctldev = NULL; /* Loop over the pin controllers */ - mutex_lock(&pinctrldev_list_mutex); list_for_each_entry(pctldev, &pinctrldev_list, node) { struct pinctrl_gpio_range *range; @@ -282,11 +271,9 @@ static int pinctrl_get_device_gpio_range(unsigned gpio, if (range != NULL) { *outdev = pctldev; *outrange = range; - mutex_unlock(&pinctrldev_list_mutex); return 0; } } - mutex_unlock(&pinctrldev_list_mutex); return -EINVAL; } @@ -302,9 +289,9 @@ static int pinctrl_get_device_gpio_range(unsigned gpio, void pinctrl_add_gpio_range(struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range) { - mutex_lock(&pctldev->gpio_ranges_lock); + mutex_lock(&pinctrl_mutex); list_add_tail(&range->node, &pctldev->gpio_ranges); - mutex_unlock(&pctldev->gpio_ranges_lock); + mutex_unlock(&pinctrl_mutex); } EXPORT_SYMBOL_GPL(pinctrl_add_gpio_range); @@ -316,9 +303,9 @@ EXPORT_SYMBOL_GPL(pinctrl_add_gpio_range); void pinctrl_remove_gpio_range(struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range) { - mutex_lock(&pctldev->gpio_ranges_lock); + mutex_lock(&pinctrl_mutex); list_del(&range->node); - mutex_unlock(&pctldev->gpio_ranges_lock); + mutex_unlock(&pinctrl_mutex); } EXPORT_SYMBOL_GPL(pinctrl_remove_gpio_range); @@ -368,14 +355,21 @@ int pinctrl_request_gpio(unsigned gpio) int ret; int pin; + mutex_lock(&pinctrl_mutex); + ret = pinctrl_get_device_gpio_range(gpio, &pctldev, &range); - if (ret) + if (ret) { + mutex_unlock(&pinctrl_mutex); return -EINVAL; + } /* Convert to the pin controllers number space */ pin = gpio - range->base + range->pin_base; - return pinmux_request_gpio(pctldev, range, pin, gpio); + ret = pinmux_request_gpio(pctldev, range, pin, gpio); + + mutex_unlock(&pinctrl_mutex); + return ret; } EXPORT_SYMBOL_GPL(pinctrl_request_gpio); @@ -394,14 +388,20 @@ void pinctrl_free_gpio(unsigned gpio) int ret; int pin; + mutex_lock(&pinctrl_mutex); + ret = pinctrl_get_device_gpio_range(gpio, &pctldev, &range); - if (ret) + if (ret) { + mutex_unlock(&pinctrl_mutex); return; + } /* Convert to the pin controllers number space */ pin = gpio - range->base + range->pin_base; - return pinmux_free_gpio(pctldev, pin, range); + pinmux_free_gpio(pctldev, pin, range); + + mutex_unlock(&pinctrl_mutex); } EXPORT_SYMBOL_GPL(pinctrl_free_gpio); @@ -432,7 +432,11 @@ static int pinctrl_gpio_direction(unsigned gpio, bool input) */ int pinctrl_gpio_direction_input(unsigned gpio) { - return pinctrl_gpio_direction(gpio, true); + int ret; + mutex_lock(&pinctrl_mutex); + ret = pinctrl_gpio_direction(gpio, true); + mutex_unlock(&pinctrl_mutex); + return ret; } EXPORT_SYMBOL_GPL(pinctrl_gpio_direction_input); @@ -446,7 +450,11 @@ EXPORT_SYMBOL_GPL(pinctrl_gpio_direction_input); */ int pinctrl_gpio_direction_output(unsigned gpio) { - return pinctrl_gpio_direction(gpio, false); + int ret; + mutex_lock(&pinctrl_mutex); + ret = pinctrl_gpio_direction(gpio, false); + mutex_unlock(&pinctrl_mutex); + return ret; } EXPORT_SYMBOL_GPL(pinctrl_gpio_direction_output); @@ -479,7 +487,6 @@ static struct pinctrl *pinctrl_get_locked(struct device *dev, const char *name) dev_err(dev, "failed to alloc struct pinctrl\n"); return ERR_PTR(-ENOMEM); } - mutex_init(&p->mutex); pinmux_init_pinctrl_handle(p); /* Iterate over the pin control maps to locate the right ones */ @@ -531,9 +538,7 @@ static struct pinctrl *pinctrl_get_locked(struct device *dev, const char *name) num_maps, devname, name ? name : "(undefined)"); /* Add the pinmux to the global list */ - mutex_lock(&pinctrl_list_mutex); list_add_tail(&p->node, &pinctrl_list); - mutex_unlock(&pinctrl_list_mutex); return p; } @@ -549,74 +554,91 @@ struct pinctrl *pinctrl_get(struct device *dev, const char *name) { struct pinctrl *p; - mutex_lock(&pinctrl_maps_mutex); + mutex_lock(&pinctrl_mutex); p = pinctrl_get_locked(dev, name); - mutex_unlock(&pinctrl_maps_mutex); + mutex_unlock(&pinctrl_mutex); return p; } EXPORT_SYMBOL_GPL(pinctrl_get); -/** - * pinctrl_put() - release a previously claimed pin control handle - * @p: a pin control handle previously claimed by pinctrl_get() - */ -void pinctrl_put(struct pinctrl *p) +static void pinctrl_put_locked(struct pinctrl *p) { if (p == NULL) return; - mutex_lock(&p->mutex); if (p->usecount) pr_warn("releasing pin control handle with active users!\n"); /* Free the groups and all acquired pins */ pinmux_put(p); - mutex_unlock(&p->mutex); /* Remove from list */ - mutex_lock(&pinctrl_list_mutex); list_del(&p->node); - mutex_unlock(&pinctrl_list_mutex); kfree(p); } -EXPORT_SYMBOL_GPL(pinctrl_put); /** - * pinctrl_enable() - enable a certain pin controller setting - * @p: the pin control handle to enable, previously claimed by pinctrl_get() + * pinctrl_put() - release a previously claimed pin control handle + * @p: a pin control handle previously claimed by pinctrl_get() */ -int pinctrl_enable(struct pinctrl *p) +void pinctrl_put(struct pinctrl *p) +{ + mutex_lock(&pinctrl_mutex); + pinctrl_put(p); + mutex_unlock(&pinctrl_mutex); +} +EXPORT_SYMBOL_GPL(pinctrl_put); + +static int pinctrl_enable_locked(struct pinctrl *p) { int ret = 0; if (p == NULL) return -EINVAL; - mutex_lock(&p->mutex); + if (p->usecount++ == 0) { ret = pinmux_enable(p); if (ret) p->usecount--; } - mutex_unlock(&p->mutex); + return ret; } -EXPORT_SYMBOL_GPL(pinctrl_enable); /** - * pinctrl_disable() - disable a certain pin control setting - * @p: the pin control handle to disable, previously claimed by pinctrl_get() + * pinctrl_enable() - enable a certain pin controller setting + * @p: the pin control handle to enable, previously claimed by pinctrl_get() */ -void pinctrl_disable(struct pinctrl *p) +int pinctrl_enable(struct pinctrl *p) +{ + int ret; + mutex_lock(&pinctrl_mutex); + ret = pinctrl_enable_locked(p); + mutex_unlock(&pinctrl_mutex); + return ret; +} +EXPORT_SYMBOL_GPL(pinctrl_enable); + +static void pinctrl_disable_locked(struct pinctrl *p) { if (p == NULL) return; - mutex_lock(&p->mutex); if (--p->usecount == 0) { pinmux_disable(p); } - mutex_unlock(&p->mutex); +} + +/** + * pinctrl_disable() - disable a certain pin control setting + * @p: the pin control handle to disable, previously claimed by pinctrl_get() + */ +void pinctrl_disable(struct pinctrl *p) +{ + mutex_lock(&pinctrl_mutex); + pinctrl_disable_locked(p); + mutex_unlock(&pinctrl_mutex); } EXPORT_SYMBOL_GPL(pinctrl_disable); @@ -676,9 +698,9 @@ int pinctrl_register_mappings(struct pinctrl_map const *maps, return -ENOMEM; } - mutex_lock(&pinctrl_maps_mutex); + mutex_lock(&pinctrl_mutex); list_add_tail(&maps_node->node, &pinctrl_maps); - mutex_unlock(&pinctrl_maps_mutex); + mutex_unlock(&pinctrl_mutex); return 0; } @@ -693,6 +715,8 @@ static int pinctrl_pins_show(struct seq_file *s, void *what) seq_printf(s, "registered pins: %d\n", pctldev->desc->npins); + mutex_lock(&pinctrl_mutex); + /* The pin number can be retrived from the pin controller descriptor */ for (i = 0; i < pctldev->desc->npins; i++) { struct pin_desc *desc; @@ -713,6 +737,8 @@ static int pinctrl_pins_show(struct seq_file *s, void *what) seq_puts(s, "\n"); } + mutex_unlock(&pinctrl_mutex); + return 0; } @@ -726,6 +752,8 @@ static int pinctrl_groups_show(struct seq_file *s, void *what) if (!ops) return 0; + mutex_lock(&pinctrl_mutex); + seq_puts(s, "registered pin groups:\n"); while (ops->list_groups(pctldev, selector) >= 0) { const unsigned *pins; @@ -748,6 +776,7 @@ static int pinctrl_groups_show(struct seq_file *s, void *what) selector++; } + mutex_unlock(&pinctrl_mutex); return 0; } @@ -759,8 +788,9 @@ static int pinctrl_gpioranges_show(struct seq_file *s, void *what) seq_puts(s, "GPIO ranges handled:\n"); + mutex_lock(&pinctrl_mutex); + /* Loop over the ranges */ - mutex_lock(&pctldev->gpio_ranges_lock); list_for_each_entry(range, &pctldev->gpio_ranges, node) { seq_printf(s, "%u: %s GPIOS [%u - %u] PINS [%u - %u]\n", range->id, range->name, @@ -768,7 +798,8 @@ static int pinctrl_gpioranges_show(struct seq_file *s, void *what) range->pin_base, (range->pin_base + range->npins - 1)); } - mutex_unlock(&pctldev->gpio_ranges_lock); + + mutex_unlock(&pinctrl_mutex); return 0; } @@ -778,7 +809,9 @@ static int pinctrl_devices_show(struct seq_file *s, void *what) struct pinctrl_dev *pctldev; seq_puts(s, "name [pinmux] [pinconf]\n"); - mutex_lock(&pinctrldev_list_mutex); + + mutex_lock(&pinctrl_mutex); + list_for_each_entry(pctldev, &pinctrldev_list, node) { seq_printf(s, "%s ", pctldev->desc->name); if (pctldev->desc->pmxops) @@ -791,7 +824,8 @@ static int pinctrl_devices_show(struct seq_file *s, void *what) seq_puts(s, "no"); seq_puts(s, "\n"); } - mutex_unlock(&pinctrldev_list_mutex); + + mutex_unlock(&pinctrl_mutex); return 0; } @@ -804,7 +838,8 @@ static int pinctrl_maps_show(struct seq_file *s, void *what) seq_puts(s, "Pinctrl maps:\n"); - mutex_lock(&pinctrl_maps_mutex); + mutex_lock(&pinctrl_mutex); + for_each_maps(maps_node, i, map) { seq_printf(s, "%s:\n", map->name); seq_printf(s, " device: %s\n", map->dev_name); @@ -813,7 +848,8 @@ static int pinctrl_maps_show(struct seq_file *s, void *what) seq_printf(s, " group: %s\n", map->group ? map->group : "(default)"); } - mutex_unlock(&pinctrl_maps_mutex); + + mutex_unlock(&pinctrl_mutex); return 0; } @@ -823,6 +859,9 @@ static int pinctrl_show(struct seq_file *s, void *what) struct pinctrl *p; seq_puts(s, "Requested pin control handlers their pinmux maps:\n"); + + mutex_lock(&pinctrl_mutex); + list_for_each_entry(p, &pinctrl_list, node) { struct pinctrl_dev *pctldev = p->pctldev; @@ -841,6 +880,8 @@ static int pinctrl_show(struct seq_file *s, void *what) p->dev ? dev_name(p->dev) : "(system)"); } + mutex_unlock(&pinctrl_mutex); + return 0; } @@ -1008,7 +1049,6 @@ struct pinctrl_dev *pinctrl_register(struct pinctrl_desc *pctldesc, pctldev->driver_data = driver_data; INIT_RADIX_TREE(&pctldev->pin_desc_tree, GFP_KERNEL); INIT_LIST_HEAD(&pctldev->gpio_ranges); - mutex_init(&pctldev->gpio_ranges_lock); pctldev->dev = dev; /* If we're implementing pinmuxing, check the ops for sanity */ @@ -1042,12 +1082,16 @@ struct pinctrl_dev *pinctrl_register(struct pinctrl_desc *pctldesc, goto out_err; } - mutex_lock(&pinctrldev_list_mutex); + mutex_lock(&pinctrl_mutex); + list_add_tail(&pctldev->node, &pinctrldev_list); - mutex_unlock(&pinctrldev_list_mutex); - pctldev->p = pinctrl_get(pctldev->dev, PINCTRL_STATE_DEFAULT); + + pctldev->p = pinctrl_get_locked(pctldev->dev, PINCTRL_STATE_DEFAULT); if (!IS_ERR(pctldev->p)) - pinctrl_enable(pctldev->p); + pinctrl_enable_locked(pctldev->p); + + mutex_unlock(&pinctrl_mutex); + pinctrl_init_device_debugfs(pctldev); return pctldev; @@ -1070,18 +1114,22 @@ void pinctrl_unregister(struct pinctrl_dev *pctldev) return; pinctrl_remove_device_debugfs(pctldev); + + mutex_lock(&pinctrl_mutex); + if (!IS_ERR(pctldev->p)) { - pinctrl_disable(pctldev->p); - pinctrl_put(pctldev->p); + pinctrl_disable_locked(pctldev->p); + pinctrl_put_locked(pctldev->p); } + /* TODO: check that no pinmuxes are still active? */ - mutex_lock(&pinctrldev_list_mutex); list_del(&pctldev->node); - mutex_unlock(&pinctrldev_list_mutex); /* Destroy descriptor tree */ pinctrl_free_pindescs(pctldev, pctldev->desc->pins, pctldev->desc->npins); kfree(pctldev); + + mutex_unlock(&pinctrl_mutex); } EXPORT_SYMBOL_GPL(pinctrl_unregister); diff --git a/drivers/pinctrl/core.h b/drivers/pinctrl/core.h index e1dfdb3c144f..8808f25a07d4 100644 --- a/drivers/pinctrl/core.h +++ b/drivers/pinctrl/core.h @@ -9,6 +9,8 @@ * License terms: GNU General Public License (GPL) version 2 */ +#include +#include #include struct pinctrl_gpio_range; @@ -22,7 +24,6 @@ struct pinctrl_gpio_range; * this radix tree * @gpio_ranges: a list of GPIO ranges that is handled by this pin controller, * ranges are added to this list at runtime - * @gpio_ranges_lock: lock for the GPIO ranges list * @dev: the device entry for this pin controller * @owner: module providing the pin controller, used for refcounting * @driver_data: driver data for drivers registering to the pin controller @@ -35,7 +36,6 @@ struct pinctrl_dev { struct pinctrl_desc *desc; struct radix_tree_root pin_desc_tree; struct list_head gpio_ranges; - struct mutex gpio_ranges_lock; struct device *dev; struct module *owner; void *driver_data; @@ -52,7 +52,6 @@ struct pinctrl_dev { * @usecount: the number of active users of this pin controller setting, used * to keep track of nested use cases * @pctldev: pin control device handling this pin control handle - * @mutex: a lock for the pin control state holder * @groups: the group selectors for the pinmux device and * selector combination handling this pinmux, this is a list that * will be traversed on all pinmux operations such as @@ -63,7 +62,6 @@ struct pinctrl { struct device *dev; unsigned usecount; struct pinctrl_dev *pctldev; - struct mutex mutex; #ifdef CONFIG_PINMUX struct list_head groups; #endif @@ -75,14 +73,12 @@ struct pinctrl { * @name: a name for the pin, e.g. the name of the pin/pad/finger on a * datasheet or such * @dynamic_name: if the name of this pin was dynamically allocated - * @lock: a lock to protect the descriptor structure * @owner: the device holding this pin or NULL of no device has claimed it */ struct pin_desc { struct pinctrl_dev *pctldev; const char *name; bool dynamic_name; - spinlock_t lock; /* These fields only added when supporting pinmux drivers */ #ifdef CONFIG_PINMUX const char *owner; @@ -99,3 +95,5 @@ static inline struct pin_desc *pin_desc_get(struct pinctrl_dev *pctldev, { return radix_tree_lookup(&pctldev->pin_desc_tree, pin); } + +extern struct mutex pinctrl_mutex; diff --git a/drivers/pinctrl/pinconf.c b/drivers/pinctrl/pinconf.c index 3f018a1cc14b..e0a453790a40 100644 --- a/drivers/pinctrl/pinconf.c +++ b/drivers/pinctrl/pinconf.c @@ -64,15 +64,23 @@ int pin_config_get(const char *dev_name, const char *name, struct pinctrl_dev *pctldev; int pin; + mutex_lock(&pinctrl_mutex); + pctldev = get_pinctrl_dev_from_devname(dev_name); - if (!pctldev) - return -EINVAL; + if (!pctldev) { + pin = -EINVAL; + goto unlock; + } pin = pin_get_from_name(pctldev, name); if (pin < 0) - return pin; + goto unlock; - return pin_config_get_for_pin(pctldev, pin, config); + pin = pin_config_get_for_pin(pctldev, pin, config); + +unlock: + mutex_unlock(&pinctrl_mutex); + return pin; } EXPORT_SYMBOL(pin_config_get); @@ -110,17 +118,27 @@ int pin_config_set(const char *dev_name, const char *name, unsigned long config) { struct pinctrl_dev *pctldev; - int pin; + int pin, ret; + + mutex_lock(&pinctrl_mutex); pctldev = get_pinctrl_dev_from_devname(dev_name); - if (!pctldev) - return -EINVAL; + if (!pctldev) { + ret = -EINVAL; + goto unlock; + } pin = pin_get_from_name(pctldev, name); - if (pin < 0) - return pin; + if (pin < 0) { + ret = pin; + goto unlock; + } + + ret = pin_config_set_for_pin(pctldev, pin, config); - return pin_config_set_for_pin(pctldev, pin, config); +unlock: + mutex_unlock(&pinctrl_mutex); + return ret; } EXPORT_SYMBOL(pin_config_set); @@ -129,25 +147,36 @@ int pin_config_group_get(const char *dev_name, const char *pin_group, { struct pinctrl_dev *pctldev; const struct pinconf_ops *ops; - int selector; + int selector, ret; + + mutex_lock(&pinctrl_mutex); pctldev = get_pinctrl_dev_from_devname(dev_name); - if (!pctldev) - return -EINVAL; + if (!pctldev) { + ret = -EINVAL; + goto unlock; + } ops = pctldev->desc->confops; if (!ops || !ops->pin_config_group_get) { dev_err(pctldev->dev, "cannot get configuration for pin " "group, missing group config get function in " "driver\n"); - return -EINVAL; + ret = -EINVAL; + goto unlock; } selector = pinctrl_get_group_selector(pctldev, pin_group); - if (selector < 0) - return selector; + if (selector < 0) { + ret = selector; + goto unlock; + } - return ops->pin_config_group_get(pctldev, selector, config); + ret = ops->pin_config_group_get(pctldev, selector, config); + +unlock: + mutex_unlock(&pinctrl_mutex); + return ret; } EXPORT_SYMBOL(pin_config_group_get); @@ -163,27 +192,34 @@ int pin_config_group_set(const char *dev_name, const char *pin_group, int ret; int i; + mutex_lock(&pinctrl_mutex); + pctldev = get_pinctrl_dev_from_devname(dev_name); - if (!pctldev) - return -EINVAL; + if (!pctldev) { + ret = -EINVAL; + goto unlock; + } ops = pctldev->desc->confops; pctlops = pctldev->desc->pctlops; if (!ops || (!ops->pin_config_group_set && !ops->pin_config_set)) { dev_err(pctldev->dev, "cannot configure pin group, missing " "config function in driver\n"); - return -EINVAL; + ret = -EINVAL; + goto unlock; } selector = pinctrl_get_group_selector(pctldev, pin_group); - if (selector < 0) - return selector; + if (selector < 0) { + ret = selector; + goto unlock; + } ret = pctlops->get_group_pins(pctldev, selector, &pins, &num_pins); if (ret) { dev_err(pctldev->dev, "cannot configure pin group, error " "getting pins\n"); - return ret; + goto unlock; } /* @@ -197,23 +233,30 @@ int pin_config_group_set(const char *dev_name, const char *pin_group, * pin-by-pin as well, it returns -EAGAIN. */ if (ret != -EAGAIN) - return ret; + goto unlock; } /* * If the controller cannot handle entire groups, we configure each pin * individually. */ - if (!ops->pin_config_set) - return 0; + if (!ops->pin_config_set) { + ret = 0; + goto unlock; + } for (i = 0; i < num_pins; i++) { ret = ops->pin_config_set(pctldev, pins[i], config); if (ret < 0) - return ret; + goto unlock; } - return 0; + ret = 0; + +unlock: + mutex_unlock(&pinctrl_mutex); + + return ret; } EXPORT_SYMBOL(pin_config_group_set); @@ -236,6 +279,8 @@ static int pinconf_pins_show(struct seq_file *s, void *what) seq_puts(s, "Pin config settings per pin\n"); seq_puts(s, "Format: pin (name): pinmux setting array\n"); + mutex_lock(&pinctrl_mutex); + /* The pin number can be retrived from the pin controller descriptor */ for (i = 0; i < pctldev->desc->npins; i++) { struct pin_desc *desc; @@ -254,6 +299,8 @@ static int pinconf_pins_show(struct seq_file *s, void *what) seq_printf(s, "\n"); } + mutex_unlock(&pinctrl_mutex); + return 0; } @@ -280,6 +327,8 @@ static int pinconf_groups_show(struct seq_file *s, void *what) seq_puts(s, "Pin config settings per pin group\n"); seq_puts(s, "Format: group (name): pinmux setting array\n"); + mutex_lock(&pinctrl_mutex); + while (pctlops->list_groups(pctldev, selector) >= 0) { const char *gname = pctlops->get_group_name(pctldev, selector); @@ -290,6 +339,8 @@ static int pinconf_groups_show(struct seq_file *s, void *what) selector++; } + mutex_unlock(&pinctrl_mutex); + return 0; } diff --git a/drivers/pinctrl/pinmux.c b/drivers/pinctrl/pinmux.c index f409f161ea1d..7342c26f4246 100644 --- a/drivers/pinctrl/pinmux.c +++ b/drivers/pinctrl/pinmux.c @@ -19,8 +19,6 @@ #include #include #include -#include -#include #include #include #include @@ -96,15 +94,12 @@ static int pin_request(struct pinctrl_dev *pctldev, goto out; } - spin_lock(&desc->lock); if (desc->owner && strcmp(desc->owner, owner)) { - spin_unlock(&desc->lock); dev_err(pctldev->dev, "pin already requested\n"); goto out; } desc->owner = owner; - spin_unlock(&desc->lock); /* Let each pin increase references to this module */ if (!try_module_get(pctldev->owner)) { @@ -131,11 +126,8 @@ static int pin_request(struct pinctrl_dev *pctldev, dev_err(pctldev->dev, "->request on device %s failed for pin %d\n", pctldev->desc->name, pin); out_free_pin: - if (status) { - spin_lock(&desc->lock); + if (status) desc->owner = NULL; - spin_unlock(&desc->lock); - } out: if (status) dev_err(pctldev->dev, "pin-%d (%s) status %d\n", @@ -178,10 +170,8 @@ static const char *pin_free(struct pinctrl_dev *pctldev, int pin, else if (ops->free) ops->free(pctldev, pin); - spin_lock(&desc->lock); owner = desc->owner; desc->owner = NULL; - spin_unlock(&desc->lock); module_put(pctldev->owner); return owner; @@ -580,6 +570,8 @@ static int pinmux_functions_show(struct seq_file *s, void *what) const struct pinmux_ops *pmxops = pctldev->desc->pmxops; unsigned func_selector = 0; + mutex_lock(&pinctrl_mutex); + while (pmxops->list_functions(pctldev, func_selector) >= 0) { const char *func = pmxops->get_function_name(pctldev, func_selector); @@ -600,9 +592,10 @@ static int pinmux_functions_show(struct seq_file *s, void *what) seq_puts(s, "]\n"); func_selector++; - } + mutex_unlock(&pinctrl_mutex); + return 0; } @@ -614,6 +607,8 @@ static int pinmux_pins_show(struct seq_file *s, void *what) seq_puts(s, "Pinmux settings per pin\n"); seq_puts(s, "Format: pin (name): owner\n"); + mutex_lock(&pinctrl_mutex); + /* The pin number can be retrived from the pin controller descriptor */ for (i = 0; i < pctldev->desc->npins; i++) { struct pin_desc *desc; @@ -635,6 +630,8 @@ static int pinmux_pins_show(struct seq_file *s, void *what) is_hog ? " (HOG)" : ""); } + mutex_unlock(&pinctrl_mutex); + return 0; } diff --git a/include/linux/pinctrl/pinctrl.h b/include/linux/pinctrl/pinctrl.h index 411fe232adf1..bbdd7e16bada 100644 --- a/include/linux/pinctrl/pinctrl.h +++ b/include/linux/pinctrl/pinctrl.h @@ -15,7 +15,6 @@ #ifdef CONFIG_PINCTRL #include -#include #include #include -- cgit v1.2.3 From 6e5e959dde0d92d177f035652aeaa77f9330c9c6 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Fri, 2 Mar 2012 13:05:47 -0700 Subject: pinctrl: API changes to support multiple states per device The API model is changed from: p = pinctrl_get(dev, "state1"); pinctrl_enable(p); ... pinctrl_disable(p); pinctrl_put(p); p = pinctrl_get(dev, "state2"); pinctrl_enable(p); ... pinctrl_disable(p); pinctrl_put(p); to this: p = pinctrl_get(dev); s1 = pinctrl_lookup_state(p, "state1"); s2 = pinctrl_lookup_state(p, "state2"); pinctrl_select_state(p, s1); ... pinctrl_select_state(p, s2); ... pinctrl_put(p); This allows devices to directly transition between states without disabling the pin controller programming and put()/get()ing the configuration data each time. This model will also better suit pinconf programming, which doesn't have a concept of "disable". The special-case hogging feature of pin controllers is re-written to use the regular APIs instead of special-case code. Hence, the pinmux-hogs debugfs file is removed; see the top-level pinctrl-handles files for equivalent data. Signed-off-by: Stephen Warren Acked-by: Dong Aisheng Signed-off-by: Linus Walleij --- Documentation/pinctrl.txt | 120 ++++++++----- arch/arm/mach-u300/core.c | 16 +- drivers/pinctrl/core.c | 363 +++++++++++++++++++++++--------------- drivers/pinctrl/core.h | 23 ++- drivers/tty/serial/sirfsoc_uart.c | 12 +- include/linux/pinctrl/consumer.h | 55 +++++- include/linux/pinctrl/machine.h | 11 +- 7 files changed, 375 insertions(+), 225 deletions(-) (limited to 'include/linux') diff --git a/Documentation/pinctrl.txt b/Documentation/pinctrl.txt index 558aac554d09..23426c7bc8dc 100644 --- a/Documentation/pinctrl.txt +++ b/Documentation/pinctrl.txt @@ -847,8 +847,8 @@ As it is possible to map a function to different groups of pins an optional This example mapping is used to switch between two positions for spi0 at runtime, as described further below under the heading "Runtime pinmuxing". -Further it is possible to match several groups of pins to the same function -for a single device, say for example in the mmc0 example above, where you can +Further it is possible for one named state to affect the muxing of several +groups of pins, say for example in the mmc0 example above, where you can additively expand the mmc0 bus from 2 to 4 to 8 pins. If we want to use all three groups for a total of 2+2+4 = 8 pins (for an 8-bit MMC bus as is the case), we define a mapping like this: @@ -879,6 +879,7 @@ case), we define a mapping like this: .dev_name = "foo-mmc.0", .name = "8bit" .ctrl_dev_name = "pinctrl-foo", + .function = "mmc0", .group = "mmc0_1_grp", }, { @@ -900,10 +901,16 @@ case), we define a mapping like this: The result of grabbing this mapping from the device with something like this (see next paragraph): - p = pinctrl_get(&device, "8bit"); + p = pinctrl_get(dev); + s = pinctrl_lookup_state(p, "8bit"); + ret = pinctrl_select_state(p, s); + +or more simply: + + p = pinctrl_get_select(dev, "8bit"); Will be that you activate all the three bottom records in the mapping at -once. Since they share the same name, pin controller device, funcion and +once. Since they share the same name, pin controller device, function and device, and since we allow multiple groups to match to a single device, they all get selected, and they all get enabled and disable simultaneously by the pinmux core. @@ -925,45 +932,63 @@ default state like this: struct foo_state { struct pinctrl *p; + struct pinctrl_state *s; ... }; foo_probe() { - /* Allocate a state holder named "state" etc */ - struct pinctrl p; + /* Allocate a state holder named "foo" etc */ + struct foo_state *foo = ...; + + foo->p = pinctrl_get(&device); + if (IS_ERR(foo->p)) { + /* FIXME: clean up "foo" here */ + return PTR_ERR(foo->p); + } - p = pinctrl_get(&device, PINCTRL_STATE_DEFAULT); - if IS_ERR(p) - return PTR_ERR(p); - pinctrl_enable(p); + foo->s = pinctrl_lookup_state(foo->p, PINCTRL_STATE_DEFAULT); + if (IS_ERR(foo->s)) { + pinctrl_put(foo->p); + /* FIXME: clean up "foo" here */ + return PTR_ERR(s); + } - state->p = p; + ret = pinctrl_select_state(foo->s); + if (ret < 0) { + pinctrl_put(foo->p); + /* FIXME: clean up "foo" here */ + return ret; + } } foo_remove() { - pinctrl_disable(state->p); pinctrl_put(state->p); } -This get/enable/disable/put sequence can just as well be handled by bus drivers +This get/lookup/select/put sequence can just as well be handled by bus drivers if you don't want each and every driver to handle it and you know the arrangement on your bus. -The semantics of the get/enable respective disable/put is as follows: +The semantics of the pinctrl APIs are: + +- pinctrl_get() is called in process context to obtain a handle to all pinctrl + information for a given client device. It will allocate a struct from the + kernel memory to hold the pinmux state. All mapping table parsing or similar + slow operations take place within this API. -- pinctrl_get() is called in process context to reserve the pins affected with - a certain mapping and set up the pinmux core and the driver. It will allocate - a struct from the kernel memory to hold the pinmux state. +- pinctrl_lookup_state() is called in process context to obtain a handle to a + specific state for a the client device. This operation may be slow too. -- pinctrl_enable()/pinctrl_disable() is quick and can be called from fastpath - (irq context) when you quickly want to set up/tear down the hardware muxing - when running a device driver. Usually it will just poke some values into a - register. +- pinctrl_select_state() programs pin controller hardware according to the + definition of the state as given by the mapping table. In theory this is a + fast-path operation, since it only involved blasting some register settings + into hardware. However, note that some pin controllers may have their + registers on a slow/IRQ-based bus, so client devices should not assume they + can call pinctrl_select_state() from non-blocking contexts. -- pinctrl_disable() is called in process context to tear down the pin requests - and release the state holder struct for the mux setting etc. +- pinctrl_put() frees all information associated with a pinctrl handle. Usually the pin control core handled the get/put pair and call out to the device drivers bookkeeping operations, like checking available functions and @@ -979,12 +1004,12 @@ System pin control hogging ========================== Pin control map entries can be hogged by the core when the pin controller -is registered. This means that the core will attempt to call pinctrl_get() and -pinctrl_enable() on it immediately after the pin control device has been -registered. +is registered. This means that the core will attempt to call pinctrl_get(), +lookup_state() and select_state() on it immediately after the pin control +device has been registered. -This is enabled by simply setting the .dev_name field in the map to the name -of the pin controller itself, like this: +This occurs for mapping table entries where the client device name is equal +to the pin controller device name, and the state name is PINCTRL_STATE_DEFAULT. { .dev_name = "pinctrl-foo", @@ -1009,8 +1034,8 @@ It is possible to mux a certain function in and out at runtime, say to move an SPI port from one set of pins to another set of pins. Say for example for spi0 in the example above, we expose two different groups of pins for the same function, but with different named in the mapping as described under -"Advanced mapping" above. So we have two mappings named "spi0-pos-A" and -"spi0-pos-B". +"Advanced mapping" above. So that for an SPI device, we have two states named +"pos-A" and "pos-B". This snippet first muxes the function in the pins defined by group A, enables it, disables and releases it, and muxes it in on the pins defined by group B: @@ -1020,23 +1045,36 @@ it, disables and releases it, and muxes it in on the pins defined by group B: foo_switch() { struct pinctrl *p; + struct pinctrl_state *s1, *s2; + + /* Setup */ + p = pinctrl_get(&device); + if (IS_ERR(p)) + ... + + s1 = pinctrl_lookup_state(foo->p, "pos-A"); + if (IS_ERR(s1)) + ... + + s2 = pinctrl_lookup_state(foo->p, "pos-B"); + if (IS_ERR(s2)) + ... /* Enable on position A */ - p = pinctrl_get(&device, "spi0-pos-A"); - if IS_ERR(p) - return PTR_ERR(p); - pinctrl_enable(p); + ret = pinctrl_select_state(s1); + if (ret < 0) + ... - /* This releases the pins again */ - pinctrl_disable(p); - pinctrl_put(p); + ... /* Enable on position B */ - p = pinctrl_get(&device, "spi0-pos-B"); - if IS_ERR(p) - return PTR_ERR(p); - pinctrl_enable(p); + ret = pinctrl_select_state(s2); + if (ret < 0) + ... + ... + + pinctrl_put(p); } The above has to be done from process context. diff --git a/arch/arm/mach-u300/core.c b/arch/arm/mach-u300/core.c index f29565a10e2e..c092cf92e8ea 100644 --- a/arch/arm/mach-u300/core.c +++ b/arch/arm/mach-u300/core.c @@ -1618,22 +1618,18 @@ static struct pinctrl_map __initdata u300_pinmux_map[] = { }; struct u300_mux_hog { - const char *name; struct device *dev; struct pinctrl *p; }; static struct u300_mux_hog u300_mux_hogs[] = { { - .name = "uart0", .dev = &uart0_device.dev, }, { - .name = "spi0", .dev = &pl022_device.dev, }, { - .name = "mmc0", .dev = &mmcsd_device.dev, }, }; @@ -1646,16 +1642,10 @@ static int __init u300_pinctrl_fetch(void) struct pinctrl *p; int ret; - p = pinctrl_get(u300_mux_hogs[i].dev, PINCTRL_STATE_DEFAULT); + p = pinctrl_get_select_default(u300_mux_hogs[i].dev); if (IS_ERR(p)) { - pr_err("u300: could not get pinmux hog %s\n", - u300_mux_hogs[i].name); - continue; - } - ret = pinctrl_enable(p); - if (ret) { - pr_err("u300: could enable pinmux hog %s\n", - u300_mux_hogs[i].name); + pr_err("u300: could not get pinmux hog for dev %s\n", + dev_name(u300_mux_hogs[i].dev)); continue; } u300_mux_hogs[i].p = p; diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c index 535f8d53c289..c6f3ca32189e 100644 --- a/drivers/pinctrl/core.c +++ b/drivers/pinctrl/core.c @@ -458,25 +458,98 @@ int pinctrl_gpio_direction_output(unsigned gpio) } EXPORT_SYMBOL_GPL(pinctrl_gpio_direction_output); -static struct pinctrl *pinctrl_get_locked(struct device *dev, const char *name) +static struct pinctrl_state *find_state(struct pinctrl *p, + const char *name) { - struct pinctrl_dev *pctldev; - const char *devname; - struct pinctrl *p; - unsigned num_maps = 0; - int ret; - struct pinctrl_maps *maps_node; - int i; - struct pinctrl_map const *map; + struct pinctrl_state *state; + + list_for_each_entry(state, &p->states, node) + if (!strcmp(state->name, name)) + return state; + + return NULL; +} + +static struct pinctrl_state *create_state(struct pinctrl *p, + const char *name) +{ + struct pinctrl_state *state; + + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (state == NULL) { + dev_err(p->dev, + "failed to alloc struct pinctrl_state\n"); + return ERR_PTR(-ENOMEM); + } + + state->name = name; + INIT_LIST_HEAD(&state->settings); + + list_add_tail(&state->node, &p->states); + + return state; +} + +static int add_setting(struct pinctrl *p, struct pinctrl_map const *map) +{ + struct pinctrl_state *state; struct pinctrl_setting *setting; + int ret; - /* We must have both a dev and state name */ - if (WARN_ON(!dev || !name)) - return ERR_PTR(-EINVAL); + state = find_state(p, map->name); + if (!state) + state = create_state(p, map->name); + if (IS_ERR(state)) + return PTR_ERR(state); - devname = dev_name(dev); + setting = kzalloc(sizeof(*setting), GFP_KERNEL); + if (setting == NULL) { + dev_err(p->dev, + "failed to alloc struct pinctrl_setting\n"); + return -ENOMEM; + } - dev_dbg(dev, "pinctrl_get() for device %s state %s\n", devname, name); + setting->pctldev = get_pinctrl_dev_from_devname(map->ctrl_dev_name); + if (setting->pctldev == NULL) { + dev_err(p->dev, "unknown pinctrl device %s in map entry", + map->ctrl_dev_name); + kfree(setting); + /* Eventually, this should trigger deferred probe */ + return -ENODEV; + } + + ret = pinmux_map_to_setting(map, setting); + if (ret < 0) { + kfree(setting); + return ret; + } + + list_add_tail(&setting->node, &state->settings); + + return 0; +} + +static struct pinctrl *find_pinctrl(struct device *dev) +{ + struct pinctrl *p; + + list_for_each_entry(p, &pinctrldev_list, node) + if (p->dev == dev) + return p; + + return NULL; +} + +static void pinctrl_put_locked(struct pinctrl *p, bool inlist); + +static struct pinctrl *create_pinctrl(struct device *dev) +{ + struct pinctrl *p; + const char *devname; + struct pinctrl_maps *maps_node; + int i; + struct pinctrl_map const *map; + int ret; /* * create the state cookie holder struct pinctrl for each @@ -489,8 +562,9 @@ static struct pinctrl *pinctrl_get_locked(struct device *dev, const char *name) return ERR_PTR(-ENOMEM); } p->dev = dev; - p->state = name; - INIT_LIST_HEAD(&p->settings); + INIT_LIST_HEAD(&p->states); + + devname = dev_name(dev); /* Iterate over the pin control maps to locate the right ones */ for_each_maps(maps_node, i, map) { @@ -498,183 +572,179 @@ static struct pinctrl *pinctrl_get_locked(struct device *dev, const char *name) if (strcmp(map->dev_name, devname)) continue; - /* State name must be the one we're looking for */ - if (strcmp(map->name, name)) - continue; - - /* - * Try to find the pctldev given in the map - */ - pctldev = get_pinctrl_dev_from_devname(map->ctrl_dev_name); - if (!pctldev) { - dev_err(dev, "unknown pinctrl device %s in map entry", - map->ctrl_dev_name); - /* Eventually, this should trigger deferred probe */ - ret = -ENODEV; - goto error; - } - - dev_dbg(dev, "in map, found pctldev %s to handle function %s", - dev_name(pctldev->dev), map->function); - - setting = kzalloc(sizeof(*setting), GFP_KERNEL); - if (setting == NULL) { - dev_err(dev, - "failed to alloc struct pinctrl_setting\n"); - ret = -ENOMEM; - goto error; + ret = add_setting(p, map); + if (ret < 0) { + pinctrl_put_locked(p, false); + return ERR_PTR(ret); } - - setting->pctldev = pctldev; - ret = pinmux_map_to_setting(map, setting); - if (ret < 0) - goto error; - - list_add_tail(&setting->node, &p->settings); - - num_maps++; } - /* - * This may be perfectly legitimate. An IP block may get re-used - * across SoCs. Not all of those SoCs may need pinmux settings for the - * IP block, e.g. if one SoC dedicates pins to that function but - * another doesn't. The driver won't know this, and will always - * attempt to set up the pinmux. The mapping table defines whether any - * HW programming is actually needed. - */ - if (!num_maps) - dev_info(dev, "zero maps found for mapping %s\n", name); - - dev_dbg(dev, "found %u maps for device %s state %s\n", - num_maps, devname, name ? name : "(undefined)"); - /* Add the pinmux to the global list */ list_add_tail(&p->node, &pinctrl_list); return p; +} -error: - list_for_each_entry(setting, &p->settings, node) - pinmux_free_setting(setting); +static struct pinctrl *pinctrl_get_locked(struct device *dev) +{ + struct pinctrl *p; - kfree(p); + if (WARN_ON(!dev)) + return ERR_PTR(-EINVAL); + + p = find_pinctrl(dev); + if (p != NULL) + return ERR_PTR(-EBUSY); - return ERR_PTR(ret); + p = create_pinctrl(dev); + if (IS_ERR(p)) + return p; + + return p; } /** - * pinctrl_get() - retrieves the pin controller handle for a certain device - * @dev: the device to get the pin controller handle for - * @name: an optional specific control mapping name or NULL, the name is only - * needed if you want to have more than one mapping per device, or if you - * need an anonymous pin control (not tied to any specific device) + * pinctrl_get() - retrieves the pinctrl handle for a device + * @dev: the device to obtain the handle for */ -struct pinctrl *pinctrl_get(struct device *dev, const char *name) +struct pinctrl *pinctrl_get(struct device *dev) { struct pinctrl *p; mutex_lock(&pinctrl_mutex); - p = pinctrl_get_locked(dev, name); + p = pinctrl_get_locked(dev); mutex_unlock(&pinctrl_mutex); return p; } EXPORT_SYMBOL_GPL(pinctrl_get); -static void pinctrl_put_locked(struct pinctrl *p) +static void pinctrl_put_locked(struct pinctrl *p, bool inlist) { - struct pinctrl_setting *setting, *n; - - if (p == NULL) - return; - - if (p->usecount) - pr_warn("releasing pin control handle with active users!\n"); - list_for_each_entry_safe(setting, n, &p->settings, node) { - pinmux_free_setting(setting); - list_del(&setting->node); - kfree(setting); + struct pinctrl_state *state, *n1; + struct pinctrl_setting *setting, *n2; + + list_for_each_entry_safe(state, n1, &p->states, node) { + list_for_each_entry_safe(setting, n2, &state->settings, node) { + if (state == p->state) + pinmux_disable_setting(setting); + pinmux_free_setting(setting); + list_del(&setting->node); + kfree(setting); + } + list_del(&state->node); + kfree(state); } - /* Remove from list */ - list_del(&p->node); - + if (inlist) + list_del(&p->node); kfree(p); } /** - * pinctrl_put() - release a previously claimed pin control handle - * @p: a pin control handle previously claimed by pinctrl_get() + * pinctrl_put() - release a previously claimed pinctrl handle + * @p: the pinctrl handle to release */ void pinctrl_put(struct pinctrl *p) { mutex_lock(&pinctrl_mutex); - pinctrl_put(p); + pinctrl_put_locked(p, true); mutex_unlock(&pinctrl_mutex); } EXPORT_SYMBOL_GPL(pinctrl_put); -static int pinctrl_enable_locked(struct pinctrl *p) +static struct pinctrl_state *pinctrl_lookup_state_locked(struct pinctrl *p, + const char *name) { - struct pinctrl_setting *setting; - int ret; + struct pinctrl_state *state; - if (p == NULL) - return -EINVAL; + state = find_state(p, name); + if (!state) + return ERR_PTR(-ENODEV); - if (p->usecount++ == 0) { - list_for_each_entry(setting, &p->settings, node) { - ret = pinmux_enable_setting(setting); - if (ret < 0) { - /* FIXME: Difficult to return to prev state */ - p->usecount--; - return ret; - } - } - } - - return 0; + return state; } /** - * pinctrl_enable() - enable a certain pin controller setting - * @p: the pin control handle to enable, previously claimed by pinctrl_get() + * pinctrl_lookup_state() - retrieves a state handle from a pinctrl handle + * @p: the pinctrl handle to retrieve the state from + * @name: the state name to retrieve */ -int pinctrl_enable(struct pinctrl *p) +struct pinctrl_state *pinctrl_lookup_state(struct pinctrl *p, const char *name) { - int ret; + struct pinctrl_state *s; + mutex_lock(&pinctrl_mutex); - ret = pinctrl_enable_locked(p); + s = pinctrl_lookup_state_locked(p, name); mutex_unlock(&pinctrl_mutex); - return ret; + + return s; } -EXPORT_SYMBOL_GPL(pinctrl_enable); +EXPORT_SYMBOL_GPL(pinctrl_lookup_state); -static void pinctrl_disable_locked(struct pinctrl *p) +static int pinctrl_select_state_locked(struct pinctrl *p, + struct pinctrl_state *state) { - struct pinctrl_setting *setting; + struct pinctrl_setting *setting, *setting2; + int ret; - if (p == NULL) - return; + if (p->state == state) + return 0; - if (--p->usecount == 0) { - list_for_each_entry(setting, &p->settings, node) - pinmux_disable_setting(setting); + if (p->state) { + /* + * The set of groups with a mux configuration in the old state + * may not be identical to the set of groups with a mux setting + * in the new state. While this might be unusual, it's entirely + * possible for the "user"-supplied mapping table to be written + * that way. For each group that was configured in the old state + * but not in the new state, this code puts that group into a + * safe/disabled state. + */ + list_for_each_entry(setting, &p->state->settings, node) { + bool found = false; + list_for_each_entry(setting2, &state->settings, node) { + if (setting2->group_selector == + setting->group_selector) { + found = true; + break; + } + } + if (!found) + pinmux_disable_setting(setting); + } + } + + p->state = state; + + /* Apply all the settings for the new state */ + list_for_each_entry(setting, &state->settings, node) { + ret = pinmux_enable_setting(setting); + if (ret < 0) { + /* FIXME: Difficult to return to prev state */ + return ret; + } } + + return 0; } /** - * pinctrl_disable() - disable a certain pin control setting - * @p: the pin control handle to disable, previously claimed by pinctrl_get() + * pinctrl_select() - select/activate/program a pinctrl state to HW + * @p: the pinctrl handle for the device that requests configuratio + * @state: the state handle to select/activate/program */ -void pinctrl_disable(struct pinctrl *p) +int pinctrl_select_state(struct pinctrl *p, struct pinctrl_state *state) { + int ret; + mutex_lock(&pinctrl_mutex); - pinctrl_disable_locked(p); + ret = pinctrl_select_state_locked(p, state); mutex_unlock(&pinctrl_mutex); + + return ret; } -EXPORT_SYMBOL_GPL(pinctrl_disable); +EXPORT_SYMBOL_GPL(pinctrl_select_state); /** * pinctrl_register_mappings() - register a set of pin controller mappings @@ -891,6 +961,7 @@ static int pinctrl_maps_show(struct seq_file *s, void *what) static int pinctrl_show(struct seq_file *s, void *what) { struct pinctrl *p; + struct pinctrl_state *state; struct pinctrl_setting *setting; seq_puts(s, "Requested pin control handlers their pinmux maps:\n"); @@ -898,12 +969,17 @@ static int pinctrl_show(struct seq_file *s, void *what) mutex_lock(&pinctrl_mutex); list_for_each_entry(p, &pinctrl_list, node) { - seq_printf(s, "device: %s state: %s users: %u\n", - dev_name(p->dev), p->state, p->usecount); + seq_printf(s, "device: %s current state: %s\n", + dev_name(p->dev), + p->state ? p->state->name : "none"); + + list_for_each_entry(state, &p->states, node) { + seq_printf(s, " state: %s\n", state->name); - list_for_each_entry(setting, &p->settings, node) { - seq_printf(s, " "); - pinmux_dbg_show(s, setting); + list_for_each_entry(setting, &state->settings, node) { + seq_printf(s, " "); + pinmux_dbg_show(s, setting); + } } } @@ -1113,9 +1189,14 @@ struct pinctrl_dev *pinctrl_register(struct pinctrl_desc *pctldesc, list_add_tail(&pctldev->node, &pinctrldev_list); - pctldev->p = pinctrl_get_locked(pctldev->dev, PINCTRL_STATE_DEFAULT); - if (!IS_ERR(pctldev->p)) - pinctrl_enable_locked(pctldev->p); + pctldev->p = pinctrl_get_locked(pctldev->dev); + if (!IS_ERR(pctldev->p)) { + struct pinctrl_state *s = + pinctrl_lookup_state_locked(pctldev->p, + PINCTRL_STATE_DEFAULT); + if (!IS_ERR(s)) + pinctrl_select_state_locked(pctldev->p, s); + } mutex_unlock(&pinctrl_mutex); @@ -1144,10 +1225,8 @@ void pinctrl_unregister(struct pinctrl_dev *pctldev) mutex_lock(&pinctrl_mutex); - if (!IS_ERR(pctldev->p)) { - pinctrl_disable_locked(pctldev->p); - pinctrl_put_locked(pctldev->p); - } + if (!IS_ERR(pctldev->p)) + pinctrl_put_locked(pctldev->p, true); /* TODO: check that no pinmuxes are still active? */ list_del(&pctldev->node); diff --git a/drivers/pinctrl/core.h b/drivers/pinctrl/core.h index 0bc52ecaf710..5691d312e15a 100644 --- a/drivers/pinctrl/core.h +++ b/drivers/pinctrl/core.h @@ -49,22 +49,31 @@ struct pinctrl_dev { * struct pinctrl - per-device pin control state holder * @node: global list node * @dev: the device using this pin control handle - * @state: the state name passed to pinctrl_get() - * @usecount: the number of active users of this pin controller setting, used - * to keep track of nested use cases - * @settings: a list of settings for this device/state + * @states: a list of states for this device + * @state: the current state */ struct pinctrl { struct list_head node; struct device *dev; - const char *state; - unsigned usecount; + struct list_head states; + struct pinctrl_state *state; +}; + +/** + * struct pinctrl_state - a pinctrl state for a device + * @node: list not for struct pinctrl's @states field + * @name: the name of this state + * @settings: a list of settings for this state + */ +struct pinctrl_state { + struct list_head node; + const char *name; struct list_head settings; }; /** * struct pinctrl_setting - an individual mux setting - * @node: list node for struct pinctrl's @settings field + * @node: list node for struct pinctrl_settings's @settings field * @pctldev: pin control device handling to be programmed * @group_selector: the group selector to program * @func_selector: the function selector to program diff --git a/drivers/tty/serial/sirfsoc_uart.c b/drivers/tty/serial/sirfsoc_uart.c index 3cabb650a1c1..5b3eda2024fe 100644 --- a/drivers/tty/serial/sirfsoc_uart.c +++ b/drivers/tty/serial/sirfsoc_uart.c @@ -673,12 +673,10 @@ int sirfsoc_uart_probe(struct platform_device *pdev) port->irq = res->start; if (sirfport->hw_flow_ctrl) { - sirfport->p = pinctrl_get(&pdev->dev, PINCTRL_STATE_DEFAULT); + sirfport->p = pinctrl_get_select_default(&pdev->dev); ret = IS_ERR(sirfport->p); if (ret) goto pin_err; - - pinctrl_enable(sirfport->p); } port->ops = &sirfsoc_uart_ops; @@ -695,10 +693,8 @@ int sirfsoc_uart_probe(struct platform_device *pdev) port_err: platform_set_drvdata(pdev, NULL); - if (sirfport->hw_flow_ctrl) { - pinctrl_disable(sirfport->p); + if (sirfport->hw_flow_ctrl) pinctrl_put(sirfport->p); - } pin_err: irq_err: devm_iounmap(&pdev->dev, port->membase); @@ -711,10 +707,8 @@ static int sirfsoc_uart_remove(struct platform_device *pdev) struct sirfsoc_uart_port *sirfport = platform_get_drvdata(pdev); struct uart_port *port = &sirfport->port; platform_set_drvdata(pdev, NULL); - if (sirfport->hw_flow_ctrl) { - pinctrl_disable(sirfport->p); + if (sirfport->hw_flow_ctrl) pinctrl_put(sirfport->p); - } devm_iounmap(&pdev->dev, port->membase); uart_remove_one_port(&sirfsoc_uart_drv, port); return 0; diff --git a/include/linux/pinctrl/consumer.h b/include/linux/pinctrl/consumer.h index 30865947f2d9..9ad5896cfa0e 100644 --- a/include/linux/pinctrl/consumer.h +++ b/include/linux/pinctrl/consumer.h @@ -12,12 +12,14 @@ #ifndef __LINUX_PINCTRL_CONSUMER_H #define __LINUX_PINCTRL_CONSUMER_H +#include #include #include #include "pinctrl.h" /* This struct is private to the core and should be regarded as a cookie */ struct pinctrl; +struct pinctrl_state; #ifdef CONFIG_PINCTRL @@ -26,10 +28,13 @@ extern int pinctrl_request_gpio(unsigned gpio); extern void pinctrl_free_gpio(unsigned gpio); extern int pinctrl_gpio_direction_input(unsigned gpio); extern int pinctrl_gpio_direction_output(unsigned gpio); -extern struct pinctrl * __must_check pinctrl_get(struct device *dev, const char *name); + +extern struct pinctrl * __must_check pinctrl_get(struct device *dev); extern void pinctrl_put(struct pinctrl *p); -extern int pinctrl_enable(struct pinctrl *p); -extern void pinctrl_disable(struct pinctrl *p); +extern struct pinctrl_state * __must_check pinctrl_lookup_state( + struct pinctrl *p, + const char *name); +extern int pinctrl_select_state(struct pinctrl *p, struct pinctrl_state *s); #else /* !CONFIG_PINCTRL */ @@ -52,7 +57,7 @@ static inline int pinctrl_gpio_direction_output(unsigned gpio) return 0; } -static inline struct pinctrl * __must_check pinctrl_get(struct device *dev, const char *name) +static inline struct pinctrl * __must_check pinctrl_get(struct device *dev) { return NULL; } @@ -61,17 +66,53 @@ static inline void pinctrl_put(struct pinctrl *p) { } -static inline int pinctrl_enable(struct pinctrl *p) +static inline struct pinctrl_state * __must_check pinctrl_lookup_state( + struct pinctrl *p, + const char *name) { - return 0; + return NULL; } -static inline void pinctrl_disable(struct pinctrl *p) +static inline int pinctrl_select_state(struct pinctrl *p, + struct pinctrl_state *s) { + return 0; } #endif /* CONFIG_PINCTRL */ +static inline struct pinctrl * __must_check pinctrl_get_select( + struct device *dev, const char *name) +{ + struct pinctrl *p; + struct pinctrl_state *s; + int ret; + + p = pinctrl_get(dev); + if (IS_ERR(p)) + return p; + + s = pinctrl_lookup_state(p, name); + if (IS_ERR(s)) { + pinctrl_put(p); + return ERR_PTR(PTR_ERR(s)); + } + + ret = pinctrl_select_state(p, s); + if (ret < 0) { + pinctrl_put(p); + return ERR_PTR(ret); + } + + return p; +} + +static inline struct pinctrl * __must_check pinctrl_get_select_default( + struct device *dev) +{ + return pinctrl_get_select(dev, PINCTRL_STATE_DEFAULT); +} + #ifdef CONFIG_PINCONF extern int pin_config_get(const char *dev_name, const char *name, diff --git a/include/linux/pinctrl/machine.h b/include/linux/pinctrl/machine.h index 20e97353d5f9..05d25c8adbaf 100644 --- a/include/linux/pinctrl/machine.h +++ b/include/linux/pinctrl/machine.h @@ -21,23 +21,22 @@ * same name as the pin controllers own dev_name(), the map entry will be * hogged by the driver itself upon registration * @name: the name of this specific map entry for the particular machine. - * This is the second parameter passed to pinmux_get() when you want - * to have several mappings to the same device + * This is the parameter passed to pinmux_lookup_state() * @ctrl_dev_name: the name of the device controlling this specific mapping, * the name must be the same as in your struct device* - * @function: a function in the driver to use for this mapping, the driver - * will lookup the function referenced by this ID on the specified - * pin control device * @group: sometimes a function can map to different pin groups, so this * selects a certain specific pin group to activate for the function, if * left as NULL, the first applicable group will be used + * @function: a function in the driver to use for this mapping, the driver + * will lookup the function referenced by this ID on the specified + * pin control device */ struct pinctrl_map { const char *dev_name; const char *name; const char *ctrl_dev_name; - const char *function; const char *group; + const char *function; }; /* -- cgit v1.2.3 From 1e2082b520721734c358f776d34a069867214c8e Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Fri, 2 Mar 2012 13:05:48 -0700 Subject: pinctrl: enhance mapping table to support pin config operations The pinctrl mapping table can now contain entries to: * Set the mux function of a pin group * Apply a set of pin config options to a pin or a group This allows pinctrl_select_state() to apply pin configs settings as well as mux settings. v3: Fix find_pinctrl() to iterate over the correct list. s/_MUX_CONFIGS_/_CONFIGS_/ in mapping table macros. Fix documentation to use correct mapping table macro. v2: Added numerous extra PIN_MAP_*() special-case macros. Fixed kerneldoc typo. Delete pinctrl_get_pin_id() and replace it with pin_get_from_name(). Various minor fixes. Updates due to rebase. Signed-off-by: Stephen Warren Acked-by: Dong Aisheng Signed-off-by: Linus Walleij --- Documentation/pinctrl.txt | 76 +++++++++++++++--- arch/arm/mach-u300/core.c | 12 +-- drivers/pinctrl/core.c | 154 ++++++++++++++++++++++++++++++------- drivers/pinctrl/core.h | 35 ++++++++- drivers/pinctrl/pinconf.c | 165 ++++++++++++++++++++++++++++++++++++++++ drivers/pinctrl/pinconf.h | 40 ++++++++++ drivers/pinctrl/pinmux.c | 69 ++++++++++------- drivers/pinctrl/pinmux.h | 23 +++++- include/linux/pinctrl/machine.h | 145 +++++++++++++++++++++++++++-------- 9 files changed, 611 insertions(+), 108 deletions(-) (limited to 'include/linux') diff --git a/Documentation/pinctrl.txt b/Documentation/pinctrl.txt index 23426c7bc8dc..d97bccf46147 100644 --- a/Documentation/pinctrl.txt +++ b/Documentation/pinctrl.txt @@ -206,14 +206,21 @@ using a certain resistor value - pull up and pull down - so that the pin has a stable value when nothing is driving the rail it is connected to, or when it's unconnected. -For example, a platform may do this: +Pin configuration can be programmed either using the explicit APIs described +immediately below, or by adding configuration entries into the mapping table; +see section "Board/machine configuration" below. + +For example, a platform may do the following to pull up a pin to VDD: #include ret = pin_config_set("foo-dev", "FOO_GPIO_PIN", PLATFORM_X_PULL_UP); -To pull up a pin to VDD. The pin configuration driver implements callbacks for -changing pin configuration in the pin controller ops like this: +The format and meaning of the configuration parameter, PLATFORM_X_PULL_UP +above, is entirely defined by the pin controller driver. + +The pin configuration driver implements callbacks for changing pin +configuration in the pin controller ops like this: #include #include @@ -765,7 +772,7 @@ obtain the function "gpioN" where "N" is the global GPIO pin number if no special GPIO-handler is registered. -Pinmux board/machine configuration +Board/machine configuration ================================== Boards and machines define how a certain complete running system is put @@ -773,9 +780,9 @@ together, including how GPIOs and devices are muxed, how regulators are constrained and how the clock tree looks. Of course pinmux settings are also part of this. -A pinmux config for a machine looks pretty much like a simple regulator -configuration, so for the example array above we want to enable i2c and -spi on the second function mapping: +A pin controller configuration for a machine looks pretty much like a simple +regulator configuration, so for the example array above we want to enable i2c +and spi on the second function mapping: #include @@ -783,20 +790,23 @@ static const struct pinctrl_map __initdata mapping[] = { { .dev_name = "foo-spi.0", .name = PINCTRL_STATE_DEFAULT, + .type = PIN_MAP_TYPE_MUX_GROUP, .ctrl_dev_name = "pinctrl-foo", - .function = "spi0", + .data.mux.function = "spi0", }, { .dev_name = "foo-i2c.0", .name = PINCTRL_STATE_DEFAULT, + .type = PIN_MAP_TYPE_MUX_GROUP, .ctrl_dev_name = "pinctrl-foo", - .function = "i2c0", + .data.mux.function = "i2c0", }, { .dev_name = "foo-mmc.0", .name = PINCTRL_STATE_DEFAULT, + .type = PIN_MAP_TYPE_MUX_GROUP, .ctrl_dev_name = "pinctrl-foo", - .function = "mmc0", + .data.mux.function = "mmc0", }, }; @@ -817,7 +827,40 @@ it even more compact which assumes you want to use pinctrl-foo and position 0 for mapping, for example: static struct pinctrl_map __initdata mapping[] = { - PIN_MAP(PINCTRL_STATE_DEFAULT, "pinctrl-foo", "i2c0", "foo-i2c.0"), + PIN_MAP_MUX_GROUP("foo-i2c.o", PINCTRL_STATE_DEFAULT, "pinctrl-foo", NULL, "i2c0"), +}; + +The mapping table may also contain pin configuration entries. It's common for +each pin/group to have a number of configuration entries that affect it, so +the table entries for configuration reference an array of config parameters +and values. An example using the convenience macros is shown below: + +static unsigned long i2c_grp_configs[] = { + FOO_PIN_DRIVEN, + FOO_PIN_PULLUP, +}; + +static unsigned long i2c_pin_configs[] = { + FOO_OPEN_COLLECTOR, + FOO_SLEW_RATE_SLOW, +}; + +static struct pinctrl_map __initdata mapping[] = { + PIN_MAP_MUX_GROUP("foo-i2c.0", PINCTRL_STATE_DEFAULT, "pinctrl-foo", "i2c0", "i2c0"), + PIN_MAP_MUX_CONFIGS_GROUP("foo-i2c.0", PINCTRL_STATE_DEFAULT, "pinctrl-foo", "i2c0", i2c_grp_configs), + PIN_MAP_MUX_CONFIGS_PIN("foo-i2c.0", PINCTRL_STATE_DEFAULT, "pinctrl-foo", "i2c0scl", i2c_pin_configs), + PIN_MAP_MUX_CONFIGS_PIN("foo-i2c.0", PINCTRL_STATE_DEFAULT, "pinctrl-foo", "i2c0sda", i2c_pin_configs), +}; + +Finally, some devices expect the mapping table to contain certain specific +named states. When running on hardware that doesn't need any pin controller +configuration, the mapping table must still contain those named states, in +order to explicitly indicate that the states were provided and intended to +be empty. Table entry macro PIN_MAP_DUMMY_STATE serves the purpose of defining +a named state without causing any pin controller to be programmed: + +static struct pinctrl_map __initdata mapping[] = { + PIN_MAP_DUMMY_STATE("foo-i2c.0", PINCTRL_STATE_DEFAULT), }; @@ -831,6 +874,7 @@ As it is possible to map a function to different groups of pins an optional { .dev_name = "foo-spi.0", .name = "spi0-pos-A", + .type = PIN_MAP_TYPE_MUX_GROUP, .ctrl_dev_name = "pinctrl-foo", .function = "spi0", .group = "spi0_0_grp", @@ -838,6 +882,7 @@ As it is possible to map a function to different groups of pins an optional { .dev_name = "foo-spi.0", .name = "spi0-pos-B", + .type = PIN_MAP_TYPE_MUX_GROUP, .ctrl_dev_name = "pinctrl-foo", .function = "spi0", .group = "spi0_1_grp", @@ -857,6 +902,7 @@ case), we define a mapping like this: { .dev_name = "foo-mmc.0", .name = "2bit" + .type = PIN_MAP_TYPE_MUX_GROUP, .ctrl_dev_name = "pinctrl-foo", .function = "mmc0", .group = "mmc0_1_grp", @@ -864,6 +910,7 @@ case), we define a mapping like this: { .dev_name = "foo-mmc.0", .name = "4bit" + .type = PIN_MAP_TYPE_MUX_GROUP, .ctrl_dev_name = "pinctrl-foo", .function = "mmc0", .group = "mmc0_1_grp", @@ -871,6 +918,7 @@ case), we define a mapping like this: { .dev_name = "foo-mmc.0", .name = "4bit" + .type = PIN_MAP_TYPE_MUX_GROUP, .ctrl_dev_name = "pinctrl-foo", .function = "mmc0", .group = "mmc0_2_grp", @@ -878,6 +926,7 @@ case), we define a mapping like this: { .dev_name = "foo-mmc.0", .name = "8bit" + .type = PIN_MAP_TYPE_MUX_GROUP, .ctrl_dev_name = "pinctrl-foo", .function = "mmc0", .group = "mmc0_1_grp", @@ -885,6 +934,7 @@ case), we define a mapping like this: { .dev_name = "foo-mmc.0", .name = "8bit" + .type = PIN_MAP_TYPE_MUX_GROUP, .ctrl_dev_name = "pinctrl-foo", .function = "mmc0", .group = "mmc0_2_grp", @@ -892,6 +942,7 @@ case), we define a mapping like this: { .dev_name = "foo-mmc.0", .name = "8bit" + .type = PIN_MAP_TYPE_MUX_GROUP, .ctrl_dev_name = "pinctrl-foo", .function = "mmc0", .group = "mmc0_3_grp", @@ -1014,6 +1065,7 @@ to the pin controller device name, and the state name is PINCTRL_STATE_DEFAULT. { .dev_name = "pinctrl-foo", .name = PINCTRL_STATE_DEFAULT, + .type = PIN_MAP_TYPE_MUX_GROUP, .ctrl_dev_name = "pinctrl-foo", .function = "power_func", }, @@ -1022,7 +1074,7 @@ Since it may be common to request the core to hog a few always-applicable mux settings on the primary pin controller, there is a convenience macro for this: -PIN_MAP_SYS_HOG("pinctrl-foo", "power_func") +PIN_MAP_MUX_GROUP_HOG_DEFAULT("pinctrl-foo", NULL /* group */, "power_func") This gives the exact same result as the above construction. diff --git a/arch/arm/mach-u300/core.c b/arch/arm/mach-u300/core.c index c092cf92e8ea..f326d3136128 100644 --- a/arch/arm/mach-u300/core.c +++ b/arch/arm/mach-u300/core.c @@ -1608,13 +1608,13 @@ static struct platform_device dma_device = { /* Pinmux settings */ static struct pinctrl_map __initdata u300_pinmux_map[] = { /* anonymous maps for chip power and EMIFs */ - PIN_MAP_SYS_HOG("pinctrl-u300", "power"), - PIN_MAP_SYS_HOG("pinctrl-u300", "emif0"), - PIN_MAP_SYS_HOG("pinctrl-u300", "emif1"), + PIN_MAP_MUX_GROUP_HOG_DEFAULT("pinctrl-u300", NULL, "power"), + PIN_MAP_MUX_GROUP_HOG_DEFAULT("pinctrl-u300", NULL, "emif0"), + PIN_MAP_MUX_GROUP_HOG_DEFAULT("pinctrl-u300", NULL, "emif1"), /* per-device maps for MMC/SD, SPI and UART */ - PIN_MAP(PINCTRL_STATE_DEFAULT, "pinctrl-u300", "mmc0", "mmci"), - PIN_MAP(PINCTRL_STATE_DEFAULT, "pinctrl-u300", "spi0", "pl022"), - PIN_MAP(PINCTRL_STATE_DEFAULT, "pinctrl-u300", "uart0", "uart0"), + PIN_MAP_MUX_GROUP_DEFAULT("mmci", "pinctrl-u300", NULL, "mmc0"), + PIN_MAP_MUX_GROUP_DEFAULT("pl022", "pinctrl-u300", NULL, "spi0"), + PIN_MAP_MUX_GROUP_DEFAULT("uart0", "pinctrl-u300", NULL, "uart0"), }; struct u300_mux_hog { diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c index c6f3ca32189e..ec3b8cc188af 100644 --- a/drivers/pinctrl/core.c +++ b/drivers/pinctrl/core.c @@ -502,6 +502,9 @@ static int add_setting(struct pinctrl *p, struct pinctrl_map const *map) if (IS_ERR(state)) return PTR_ERR(state); + if (map->type == PIN_MAP_TYPE_DUMMY_STATE) + return 0; + setting = kzalloc(sizeof(*setting), GFP_KERNEL); if (setting == NULL) { dev_err(p->dev, @@ -509,6 +512,8 @@ static int add_setting(struct pinctrl *p, struct pinctrl_map const *map) return -ENOMEM; } + setting->type = map->type; + setting->pctldev = get_pinctrl_dev_from_devname(map->ctrl_dev_name); if (setting->pctldev == NULL) { dev_err(p->dev, "unknown pinctrl device %s in map entry", @@ -518,7 +523,18 @@ static int add_setting(struct pinctrl *p, struct pinctrl_map const *map) return -ENODEV; } - ret = pinmux_map_to_setting(map, setting); + switch (map->type) { + case PIN_MAP_TYPE_MUX_GROUP: + ret = pinmux_map_to_setting(map, setting); + break; + case PIN_MAP_TYPE_CONFIGS_PIN: + case PIN_MAP_TYPE_CONFIGS_GROUP: + ret = pinconf_map_to_setting(map, setting); + break; + default: + ret = -EINVAL; + break; + } if (ret < 0) { kfree(setting); return ret; @@ -533,7 +549,7 @@ static struct pinctrl *find_pinctrl(struct device *dev) { struct pinctrl *p; - list_for_each_entry(p, &pinctrldev_list, node) + list_for_each_entry(p, &pinctrl_list, node) if (p->dev == dev) return p; @@ -626,9 +642,19 @@ static void pinctrl_put_locked(struct pinctrl *p, bool inlist) list_for_each_entry_safe(state, n1, &p->states, node) { list_for_each_entry_safe(setting, n2, &state->settings, node) { - if (state == p->state) - pinmux_disable_setting(setting); - pinmux_free_setting(setting); + switch (setting->type) { + case PIN_MAP_TYPE_MUX_GROUP: + if (state == p->state) + pinmux_disable_setting(setting); + pinmux_free_setting(setting); + break; + case PIN_MAP_TYPE_CONFIGS_PIN: + case PIN_MAP_TYPE_CONFIGS_GROUP: + pinconf_free_setting(setting); + break; + default: + break; + } list_del(&setting->node); kfree(setting); } @@ -703,9 +729,13 @@ static int pinctrl_select_state_locked(struct pinctrl *p, */ list_for_each_entry(setting, &p->state->settings, node) { bool found = false; + if (setting->type != PIN_MAP_TYPE_MUX_GROUP) + continue; list_for_each_entry(setting2, &state->settings, node) { - if (setting2->group_selector == - setting->group_selector) { + if (setting2->type != PIN_MAP_TYPE_MUX_GROUP) + continue; + if (setting2->data.mux.group == + setting->data.mux.group) { found = true; break; } @@ -719,7 +749,18 @@ static int pinctrl_select_state_locked(struct pinctrl *p, /* Apply all the settings for the new state */ list_for_each_entry(setting, &state->settings, node) { - ret = pinmux_enable_setting(setting); + switch (setting->type) { + case PIN_MAP_TYPE_MUX_GROUP: + ret = pinmux_enable_setting(setting); + break; + case PIN_MAP_TYPE_CONFIGS_PIN: + case PIN_MAP_TYPE_CONFIGS_GROUP: + ret = pinconf_apply_setting(setting); + break; + default: + ret = -EINVAL; + break; + } if (ret < 0) { /* FIXME: Difficult to return to prev state */ return ret; @@ -756,33 +797,48 @@ EXPORT_SYMBOL_GPL(pinctrl_select_state); int pinctrl_register_mappings(struct pinctrl_map const *maps, unsigned num_maps) { - int i; + int i, ret; struct pinctrl_maps *maps_node; pr_debug("add %d pinmux maps\n", num_maps); /* First sanity check the new mapping */ for (i = 0; i < num_maps; i++) { + if (!maps[i].dev_name) { + pr_err("failed to register map %s (%d): no device given\n", + maps[i].name, i); + return -EINVAL; + } + if (!maps[i].name) { pr_err("failed to register map %d: no map name given\n", i); return -EINVAL; } - if (!maps[i].ctrl_dev_name) { + if (maps[i].type != PIN_MAP_TYPE_DUMMY_STATE && + !maps[i].ctrl_dev_name) { pr_err("failed to register map %s (%d): no pin control device given\n", maps[i].name, i); return -EINVAL; } - if (!maps[i].function) { - pr_err("failed to register map %s (%d): no function ID given\n", - maps[i].name, i); - return -EINVAL; - } - - if (!maps[i].dev_name) { - pr_err("failed to register map %s (%d): no device given\n", + switch (maps[i].type) { + case PIN_MAP_TYPE_DUMMY_STATE: + break; + case PIN_MAP_TYPE_MUX_GROUP: + ret = pinmux_validate_map(&maps[i], i); + if (ret < 0) + return 0; + break; + case PIN_MAP_TYPE_CONFIGS_PIN: + case PIN_MAP_TYPE_CONFIGS_GROUP: + ret = pinconf_validate_map(&maps[i], i); + if (ret < 0) + return 0; + break; + default: + pr_err("failed to register map %s (%d): invalid type given\n", maps[i].name, i); return -EINVAL; } @@ -934,6 +990,22 @@ static int pinctrl_devices_show(struct seq_file *s, void *what) return 0; } +static inline const char *map_type(enum pinctrl_map_type type) +{ + static const char * const names[] = { + "INVALID", + "DUMMY_STATE", + "MUX_GROUP", + "CONFIGS_PIN", + "CONFIGS_GROUP", + }; + + if (type >= ARRAY_SIZE(names)) + return "UNKNOWN"; + + return names[type]; +} + static int pinctrl_maps_show(struct seq_file *s, void *what) { struct pinctrl_maps *maps_node; @@ -945,12 +1017,27 @@ static int pinctrl_maps_show(struct seq_file *s, void *what) mutex_lock(&pinctrl_mutex); for_each_maps(maps_node, i, map) { - seq_printf(s, "%s:\n", map->name); - seq_printf(s, " device: %s\n", map->dev_name); - seq_printf(s, " controlling device %s\n", map->ctrl_dev_name); - seq_printf(s, " function: %s\n", map->function); - seq_printf(s, " group: %s\n", map->group ? map->group : - "(default)"); + seq_printf(s, "device %s\nstate %s\ntype %s (%d)\n", + map->dev_name, map->name, map_type(map->type), + map->type); + + if (map->type != PIN_MAP_TYPE_DUMMY_STATE) + seq_printf(s, "controlling device %s\n", + map->ctrl_dev_name); + + switch (map->type) { + case PIN_MAP_TYPE_MUX_GROUP: + pinmux_show_map(s, map); + break; + case PIN_MAP_TYPE_CONFIGS_PIN: + case PIN_MAP_TYPE_CONFIGS_GROUP: + pinconf_show_map(s, map); + break; + default: + break; + } + + seq_printf(s, "\n"); } mutex_unlock(&pinctrl_mutex); @@ -977,8 +1064,23 @@ static int pinctrl_show(struct seq_file *s, void *what) seq_printf(s, " state: %s\n", state->name); list_for_each_entry(setting, &state->settings, node) { - seq_printf(s, " "); - pinmux_dbg_show(s, setting); + struct pinctrl_dev *pctldev = setting->pctldev; + + seq_printf(s, " type: %s controller %s ", + map_type(setting->type), + pinctrl_dev_get_name(pctldev)); + + switch (setting->type) { + case PIN_MAP_TYPE_MUX_GROUP: + pinmux_show_setting(s, setting); + break; + case PIN_MAP_TYPE_CONFIGS_PIN: + case PIN_MAP_TYPE_CONFIGS_GROUP: + pinconf_show_setting(s, setting); + break; + default: + break; + } } } } diff --git a/drivers/pinctrl/core.h b/drivers/pinctrl/core.h index 5691d312e15a..1cae3723bbed 100644 --- a/drivers/pinctrl/core.h +++ b/drivers/pinctrl/core.h @@ -71,18 +71,45 @@ struct pinctrl_state { struct list_head settings; }; +/** + * struct pinctrl_setting_mux - setting data for MAP_TYPE_MUX_GROUP + * @group: the group selector to program + * @func: the function selector to program + */ +struct pinctrl_setting_mux { + unsigned group; + unsigned func; +}; + +/** + * struct pinctrl_setting_configs - setting data for MAP_TYPE_CONFIGS_* + * @group_or_pin: the group selector or pin ID to program + * @configs: a pointer to an array of config parameters/values to program into + * hardware. Each individual pin controller defines the format and meaning + * of config parameters. + * @num_configs: the number of entries in array @configs + */ +struct pinctrl_setting_configs { + unsigned group_or_pin; + unsigned long *configs; + unsigned num_configs; +}; + /** * struct pinctrl_setting - an individual mux setting * @node: list node for struct pinctrl_settings's @settings field + * @type: the type of setting * @pctldev: pin control device handling to be programmed - * @group_selector: the group selector to program - * @func_selector: the function selector to program + * @data: Data specific to the setting type */ struct pinctrl_setting { struct list_head node; + enum pinctrl_map_type type; struct pinctrl_dev *pctldev; - unsigned group_selector; - unsigned func_selector; + union { + struct pinctrl_setting_mux mux; + struct pinctrl_setting_configs configs; + } data; }; /** diff --git a/drivers/pinctrl/pinconf.c b/drivers/pinctrl/pinconf.c index e0a453790a40..84869f28b101 100644 --- a/drivers/pinctrl/pinconf.c +++ b/drivers/pinctrl/pinconf.c @@ -36,6 +36,24 @@ int pinconf_check_ops(struct pinctrl_dev *pctldev) return 0; } +int pinconf_validate_map(struct pinctrl_map const *map, int i) +{ + if (!map->data.configs.group_or_pin) { + pr_err("failed to register map %s (%d): no group/pin given\n", + map->name, i); + return -EINVAL; + } + + if (map->data.configs.num_configs && + !map->data.configs.configs) { + pr_err("failed to register map %s (%d): no configs ptr given\n", + map->name, i); + return -EINVAL; + } + + return 0; +} + static int pin_config_get_for_pin(struct pinctrl_dev *pctldev, unsigned pin, unsigned long *config) { @@ -260,8 +278,155 @@ unlock: } EXPORT_SYMBOL(pin_config_group_set); +int pinconf_map_to_setting(struct pinctrl_map const *map, + struct pinctrl_setting *setting) +{ + struct pinctrl_dev *pctldev = setting->pctldev; + + switch (setting->type) { + case PIN_MAP_TYPE_CONFIGS_PIN: + setting->data.configs.group_or_pin = + pin_get_from_name(pctldev, + map->data.configs.group_or_pin); + if (setting->data.configs.group_or_pin < 0) + return setting->data.configs.group_or_pin; + break; + case PIN_MAP_TYPE_CONFIGS_GROUP: + setting->data.configs.group_or_pin = + pinctrl_get_group_selector(pctldev, + map->data.configs.group_or_pin); + if (setting->data.configs.group_or_pin < 0) + return setting->data.configs.group_or_pin; + break; + default: + return -EINVAL; + } + + setting->data.configs.num_configs = map->data.configs.num_configs; + setting->data.configs.configs = map->data.configs.configs; + + return 0; +} + +void pinconf_free_setting(struct pinctrl_setting const *setting) +{ +} + +int pinconf_apply_setting(struct pinctrl_setting const *setting) +{ + struct pinctrl_dev *pctldev = setting->pctldev; + const struct pinconf_ops *ops = pctldev->desc->confops; + int i, ret; + + if (!ops) { + dev_err(pctldev->dev, "missing confops\n"); + return -EINVAL; + } + + switch (setting->type) { + case PIN_MAP_TYPE_CONFIGS_PIN: + if (!ops->pin_config_set) { + dev_err(pctldev->dev, "missing pin_config_set op\n"); + return -EINVAL; + } + for (i = 0; i < setting->data.configs.num_configs; i++) { + ret = ops->pin_config_set(pctldev, + setting->data.configs.group_or_pin, + setting->data.configs.configs[i]); + if (ret < 0) { + dev_err(pctldev->dev, + "pin_config_set op failed for pin %d config %08lx\n", + setting->data.configs.group_or_pin, + setting->data.configs.configs[i]); + return ret; + } + } + break; + case PIN_MAP_TYPE_CONFIGS_GROUP: + if (!ops->pin_config_group_set) { + dev_err(pctldev->dev, + "missing pin_config_group_set op\n"); + return -EINVAL; + } + for (i = 0; i < setting->data.configs.num_configs; i++) { + ret = ops->pin_config_group_set(pctldev, + setting->data.configs.group_or_pin, + setting->data.configs.configs[i]); + if (ret < 0) { + dev_err(pctldev->dev, + "pin_config_group_set op failed for group %d config %08lx\n", + setting->data.configs.group_or_pin, + setting->data.configs.configs[i]); + return ret; + } + } + break; + default: + return -EINVAL; + } + + return 0; +} + #ifdef CONFIG_DEBUG_FS +void pinconf_show_map(struct seq_file *s, struct pinctrl_map const *map) +{ + int i; + + switch (map->type) { + case PIN_MAP_TYPE_CONFIGS_PIN: + seq_printf(s, "pin "); + break; + case PIN_MAP_TYPE_CONFIGS_GROUP: + seq_printf(s, "group "); + break; + default: + break; + } + + seq_printf(s, "%s\n", map->data.configs.group_or_pin); + + for (i = 0; i < map->data.configs.num_configs; i++) + seq_printf(s, "config %08lx\n", map->data.configs.configs[i]); +} + +void pinconf_show_setting(struct seq_file *s, + struct pinctrl_setting const *setting) +{ + struct pinctrl_dev *pctldev = setting->pctldev; + const struct pinctrl_ops *pctlops = pctldev->desc->pctlops; + struct pin_desc *desc; + int i; + + switch (setting->type) { + case PIN_MAP_TYPE_CONFIGS_PIN: + desc = pin_desc_get(setting->pctldev, + setting->data.configs.group_or_pin); + seq_printf(s, "pin %s (%d)", + desc->name ? desc->name : "unnamed", + setting->data.configs.group_or_pin); + break; + case PIN_MAP_TYPE_CONFIGS_GROUP: + seq_printf(s, "group %s (%d)", + pctlops->get_group_name(pctldev, + setting->data.configs.group_or_pin), + setting->data.configs.group_or_pin); + break; + default: + break; + } + + /* + * FIXME: We should really get the pin controler to dump the config + * values, so they can be decoded to something meaningful. + */ + for (i = 0; i < setting->data.configs.num_configs; i++) + seq_printf(s, " %08lx", setting->data.configs.configs[i]); + + seq_printf(s, "\n"); +} + static void pinconf_dump_pin(struct pinctrl_dev *pctldev, struct seq_file *s, int pin) { diff --git a/drivers/pinctrl/pinconf.h b/drivers/pinctrl/pinconf.h index 1d6ea9de75fc..0ded227661a5 100644 --- a/drivers/pinctrl/pinconf.h +++ b/drivers/pinctrl/pinconf.h @@ -15,6 +15,16 @@ int pinconf_check_ops(struct pinctrl_dev *pctldev); +int pinconf_validate_map(struct pinctrl_map const *map, int i); + +int pinconf_map_to_setting(struct pinctrl_map const *map, + struct pinctrl_setting *setting); +void pinconf_free_setting(struct pinctrl_setting const *setting); +int pinconf_apply_setting(struct pinctrl_setting const *setting); + +void pinconf_show_map(struct seq_file *s, struct pinctrl_map const *map); +void pinconf_show_setting(struct seq_file *s, + struct pinctrl_setting const *setting); void pinconf_init_device_debugfs(struct dentry *devroot, struct pinctrl_dev *pctldev); @@ -25,6 +35,36 @@ static inline int pinconf_check_ops(struct pinctrl_dev *pctldev) return 0; } +static inline int pinconf_validate_map(struct pinctrl_map const *map, int i) +{ + return 0; +} + +static inline int pinconf_map_to_setting(struct pinctrl_map const *map, + struct pinctrl_setting *setting) +{ + return 0; +} + +static inline void pinconf_free_setting(struct pinctrl_setting const *setting) +{ +} + +static inline int pinconf_apply_setting(struct pinctrl_setting const *setting) +{ + return 0; +} + +static inline void pinconf_show_map(struct seq_file *s, + struct pinctrl_map const *map) +{ +} + +static inline void pinconf_show_setting(struct seq_file *s, + struct pinctrl_setting const *setting) +{ +} + static inline void pinconf_init_device_debugfs(struct dentry *devroot, struct pinctrl_dev *pctldev) { diff --git a/drivers/pinctrl/pinmux.c b/drivers/pinctrl/pinmux.c index 56ca42e6a6ec..4852ebe5712e 100644 --- a/drivers/pinctrl/pinmux.c +++ b/drivers/pinctrl/pinmux.c @@ -58,6 +58,17 @@ int pinmux_check_ops(struct pinctrl_dev *pctldev) return 0; } +int pinmux_validate_map(struct pinctrl_map const *map, int i) +{ + if (!map->data.mux.function) { + pr_err("failed to register map %s (%d): no function given\n", + map->name, i); + return -EINVAL; + } + + return 0; +} + /** * pin_request() - request a single pin to be muxed in, typically for GPIO * @pin: the pin number in the global pin space @@ -284,21 +295,21 @@ int pinmux_map_to_setting(struct pinctrl_map const *map, const unsigned *pins; unsigned num_pins; - setting->func_selector = - pinmux_func_name_to_selector(pctldev, map->function); - if (setting->func_selector < 0) - return setting->func_selector; + setting->data.mux.func = + pinmux_func_name_to_selector(pctldev, map->data.mux.function); + if (setting->data.mux.func < 0) + return setting->data.mux.func; - ret = pmxops->get_function_groups(pctldev, setting->func_selector, + ret = pmxops->get_function_groups(pctldev, setting->data.mux.func, &groups, &num_groups); if (ret < 0) return ret; if (!num_groups) return -EINVAL; - if (map->group) { + if (map->data.mux.group) { bool found = false; - group = map->group; + group = map->data.mux.group; for (i = 0; i < num_groups; i++) { if (!strcmp(group, groups[i])) { found = true; @@ -311,17 +322,16 @@ int pinmux_map_to_setting(struct pinctrl_map const *map, group = groups[0]; } - setting->group_selector = - pinctrl_get_group_selector(pctldev, group); - if (setting->group_selector < 0) - return setting->group_selector; + setting->data.mux.group = pinctrl_get_group_selector(pctldev, group); + if (setting->data.mux.group < 0) + return setting->data.mux.group; - ret = pctlops->get_group_pins(pctldev, setting->group_selector, - &pins, &num_pins); + ret = pctlops->get_group_pins(pctldev, setting->data.mux.group, &pins, + &num_pins); if (ret) { dev_err(pctldev->dev, "could not get pins for device %s group selector %d\n", - pinctrl_dev_get_name(pctldev), setting->group_selector); + pinctrl_dev_get_name(pctldev), setting->data.mux.group); return -ENODEV; } @@ -352,12 +362,12 @@ void pinmux_free_setting(struct pinctrl_setting const *setting) int ret; int i; - ret = pctlops->get_group_pins(pctldev, setting->group_selector, + ret = pctlops->get_group_pins(pctldev, setting->data.mux.group, &pins, &num_pins); if (ret) { dev_err(pctldev->dev, "could not get pins for device %s group selector %d\n", - pinctrl_dev_get_name(pctldev), setting->group_selector); + pinctrl_dev_get_name(pctldev), setting->data.mux.group); return; } @@ -370,8 +380,8 @@ int pinmux_enable_setting(struct pinctrl_setting const *setting) struct pinctrl_dev *pctldev = setting->pctldev; const struct pinmux_ops *ops = pctldev->desc->pmxops; - return ops->enable(pctldev, setting->func_selector, - setting->group_selector); + return ops->enable(pctldev, setting->data.mux.func, + setting->data.mux.group); } void pinmux_disable_setting(struct pinctrl_setting const *setting) @@ -379,7 +389,7 @@ void pinmux_disable_setting(struct pinctrl_setting const *setting) struct pinctrl_dev *pctldev = setting->pctldev; const struct pinmux_ops *ops = pctldev->desc->pmxops; - ops->disable(pctldev, setting->func_selector, setting->group_selector); + ops->disable(pctldev, setting->data.mux.func, setting->data.mux.group); } #ifdef CONFIG_DEBUG_FS @@ -456,18 +466,25 @@ static int pinmux_pins_show(struct seq_file *s, void *what) return 0; } -void pinmux_dbg_show(struct seq_file *s, struct pinctrl_setting const *setting) +void pinmux_show_map(struct seq_file *s, struct pinctrl_map const *map) +{ + seq_printf(s, "group %s\nfunction %s\n", + map->data.mux.group ? map->data.mux.group : "(default)", + map->data.mux.function); +} + +void pinmux_show_setting(struct seq_file *s, + struct pinctrl_setting const *setting) { struct pinctrl_dev *pctldev = setting->pctldev; const struct pinmux_ops *pmxops = pctldev->desc->pmxops; const struct pinctrl_ops *pctlops = pctldev->desc->pctlops; - seq_printf(s, "controller: %s group: %s (%u) function: %s (%u)\n", - pinctrl_dev_get_name(pctldev), - pctlops->get_group_name(pctldev, setting->group_selector), - setting->group_selector, - pmxops->get_function_name(pctldev, setting->func_selector), - setting->func_selector); + seq_printf(s, "group: %s (%u) function: %s (%u)\n", + pctlops->get_group_name(pctldev, setting->data.mux.group), + setting->data.mux.group, + pmxops->get_function_name(pctldev, setting->data.mux.func), + setting->data.mux.func); } static int pinmux_functions_open(struct inode *inode, struct file *file) diff --git a/drivers/pinctrl/pinmux.h b/drivers/pinctrl/pinmux.h index 1500ae88f87c..6fc47003e95d 100644 --- a/drivers/pinctrl/pinmux.h +++ b/drivers/pinctrl/pinmux.h @@ -14,6 +14,8 @@ int pinmux_check_ops(struct pinctrl_dev *pctldev); +int pinmux_validate_map(struct pinctrl_map const *map, int i); + int pinmux_request_gpio(struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range, unsigned pin, unsigned gpio); @@ -29,7 +31,9 @@ void pinmux_free_setting(struct pinctrl_setting const *setting); int pinmux_enable_setting(struct pinctrl_setting const *setting); void pinmux_disable_setting(struct pinctrl_setting const *setting); -void pinmux_dbg_show(struct seq_file *s, struct pinctrl_setting const *setting); +void pinmux_show_map(struct seq_file *s, struct pinctrl_map const *map); +void pinmux_show_setting(struct seq_file *s, + struct pinctrl_setting const *setting); void pinmux_init_device_debugfs(struct dentry *devroot, struct pinctrl_dev *pctldev); @@ -40,6 +44,11 @@ static inline int pinmux_check_ops(struct pinctrl_dev *pctldev) return 0; } +static inline int pinmux_validate_map(struct pinctrl_map const *map, int i) +{ + return 0; +} + static inline int pinmux_request_gpio(struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range, unsigned pin, unsigned gpio) @@ -80,12 +89,18 @@ static inline void pinmux_disable_setting( { } -static inline void pinmux_init_device_debugfs(struct dentry *devroot, - struct pinctrl_dev *pctldev) +static inline void pinmux_show_map(struct seq_file *s, + struct pinctrl_map const *map) +{ +} + +static inline void pinmux_show_setting(struct seq_file *s, + struct pinctrl_setting const *setting) { } -static inline void pinmux_dbg_show(struct seq_file *s, struct pinctrl *p) +static inline void pinmux_init_device_debugfs(struct dentry *devroot, + struct pinctrl_dev *pctldev) { } diff --git a/include/linux/pinctrl/machine.h b/include/linux/pinctrl/machine.h index 05d25c8adbaf..3fd2f9dfc645 100644 --- a/include/linux/pinctrl/machine.h +++ b/include/linux/pinctrl/machine.h @@ -14,6 +14,41 @@ #include "pinctrl.h" +enum pinctrl_map_type { + PIN_MAP_TYPE_INVALID, + PIN_MAP_TYPE_DUMMY_STATE, + PIN_MAP_TYPE_MUX_GROUP, + PIN_MAP_TYPE_CONFIGS_PIN, + PIN_MAP_TYPE_CONFIGS_GROUP, +}; + +/** + * struct pinctrl_map_mux - mapping table content for MAP_TYPE_MUX_GROUP + * @group: the name of the group whose mux function is to be configured. This + * field may be left NULL, and the first applicable group for the function + * will be used. + * @function: the mux function to select for the group + */ +struct pinctrl_map_mux { + const char *group; + const char *function; +}; + +/** + * struct pinctrl_map_configs - mapping table content for MAP_TYPE_CONFIGS_* + * @group_or_pin: the name of the pin or group whose configuration parameters + * are to be configured. + * @configs: a pointer to an array of config parameters/values to program into + * hardware. Each individual pin controller defines the format and meaning + * of config parameters. + * @num_configs: the number of entries in array @configs + */ +struct pinctrl_map_configs { + const char *group_or_pin; + unsigned long *configs; + unsigned num_configs; +}; + /** * struct pinctrl_map - boards/machines shall provide this map for devices * @dev_name: the name of the device using this specific mapping, the name @@ -22,46 +57,96 @@ * hogged by the driver itself upon registration * @name: the name of this specific map entry for the particular machine. * This is the parameter passed to pinmux_lookup_state() + * @type: the type of mapping table entry * @ctrl_dev_name: the name of the device controlling this specific mapping, - * the name must be the same as in your struct device* - * @group: sometimes a function can map to different pin groups, so this - * selects a certain specific pin group to activate for the function, if - * left as NULL, the first applicable group will be used - * @function: a function in the driver to use for this mapping, the driver - * will lookup the function referenced by this ID on the specified - * pin control device + * the name must be the same as in your struct device*. This field is not + * used for PIN_MAP_TYPE_DUMMY_STATE + * @data: Data specific to the mapping type */ struct pinctrl_map { const char *dev_name; const char *name; + enum pinctrl_map_type type; const char *ctrl_dev_name; - const char *group; - const char *function; + union { + struct pinctrl_map_mux mux; + struct pinctrl_map_configs configs; + } data; }; -/* - * Convenience macro to set a simple map from a certain pin controller and a - * certain function to a named device - */ -#define PIN_MAP(a, b, c, d) \ - { .name = a, .ctrl_dev_name = b, .function = c, .dev_name = d } +/* Convenience macros to create mapping table entries */ -/* - * Convenience macro to map a system function onto a certain pinctrl device, - * to be hogged by the pin control core until the system shuts down. - */ -#define PIN_MAP_SYS_HOG(a, b) \ - { .name = PINCTRL_STATE_DEFAULT, .ctrl_dev_name = a, .dev_name = a, \ - .function = b, } +#define PIN_MAP_DUMMY_STATE(dev, state) \ + { \ + .dev_name = dev, \ + .name = state, \ + .type = PIN_MAP_TYPE_DUMMY_STATE, \ + } -/* - * Convenience macro to map a system function onto a certain pinctrl device - * using a specified group, to be hogged by the pin control core until the - * system shuts down. - */ -#define PIN_MAP_SYS_HOG_GROUP(a, b, c) \ - { .name = PINCTRL_STATE_DEFAULT, .ctrl_dev_name = a, .dev_name = a, \ - .function = b, .group = c, } +#define PIN_MAP_MUX_GROUP(dev, state, pinctrl, grp, func) \ + { \ + .dev_name = dev, \ + .name = state, \ + .type = PIN_MAP_TYPE_MUX_GROUP, \ + .ctrl_dev_name = pinctrl, \ + .data.mux = { \ + .group = grp, \ + .function = func, \ + }, \ + } + +#define PIN_MAP_MUX_GROUP_DEFAULT(dev, pinctrl, grp, func) \ + PIN_MAP_MUX_GROUP(dev, PINCTRL_STATE_DEFAULT, pinctrl, grp, func) + +#define PIN_MAP_MUX_GROUP_HOG(dev, state, grp, func) \ + PIN_MAP_MUX_GROUP(dev, state, dev, grp, func) + +#define PIN_MAP_MUX_GROUP_HOG_DEFAULT(dev, grp, func) \ + PIN_MAP_MUX_GROUP(dev, PINCTRL_STATE_DEFAULT, dev, grp, func) + +#define PIN_MAP_CONFIGS_PIN(dev, state, pinctrl, pin, cfgs) \ + { \ + .dev_name = dev, \ + .name = state, \ + .type = PIN_MAP_TYPE_CONFIGS_PIN, \ + .ctrl_dev_name = pinctrl, \ + .data.configs = { \ + .group_or_pin = pin, \ + .configs = cfgs, \ + .num_configs = ARRAY_SIZE(cfgs), \ + }, \ + } + +#define PIN_MAP_CONFIGS_PIN_DEFAULT(dev, pinctrl, pin, cfgs) \ + PIN_MAP_CONFIGS_PIN(dev, PINCTRL_STATE_DEFAULT, pinctrl, pin, cfgs) + +#define PIN_MAP_CONFIGS_PIN_HOG(dev, state, pin, cfgs) \ + PIN_MAP_CONFIGS_PIN(dev, state, dev, pin, cfgs) + +#define PIN_MAP_CONFIGS_PIN_HOG_DEFAULT(dev, pin, cfgs) \ + PIN_MAP_CONFIGS_PIN(dev, PINCTRL_STATE_DEFAULT, dev, pin, cfgs) + +#define PIN_MAP_CONFIGS_GROUP(dev, state, pinctrl, grp, cfgs) \ + { \ + .dev_name = dev, \ + .name = state, \ + .type = PIN_MAP_TYPE_CONFIGS_GROUP, \ + .ctrl_dev_name = pinctrl, \ + .data.configs = { \ + .group_or_pin = grp, \ + .configs = cfgs, \ + .num_configs = ARRAY_SIZE(cfgs), \ + }, \ + } + +#define PIN_MAP_CONFIGS_GROUP_DEFAULT(dev, pinctrl, grp, cfgs) \ + PIN_MAP_CONFIGS_GROUP(dev, PINCTRL_STATE_DEFAULT, pinctrl, grp, cfgs) + +#define PIN_MAP_CONFIGS_GROUP_HOG(dev, state, grp, cfgs) \ + PIN_MAP_CONFIGS_GROUP(dev, state, dev, grp, cfgs) + +#define PIN_MAP_CONFIGS_GROUP_HOG_DEFAULT(dev, grp, cfgs) \ + PIN_MAP_CONFIGS_GROUP(dev, PINCTRL_STATE_DEFAULT, dev, grp, cfgs) #ifdef CONFIG_PINMUX -- cgit v1.2.3 From 91e56aaedd7ebceacde782a3921fadef4b5d0e1c Mon Sep 17 00:00:00 2001 From: Bryan Schumaker Date: Mon, 5 Mar 2012 14:58:15 -0500 Subject: NFS: Undo changes to idmap.h When compiled without NFS v4 configured these function won't be defined and the compiler will yell. Signed-off-by: Bryan Schumaker Signed-off-by: Trond Myklebust --- include/linux/nfs_idmap.h | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'include/linux') diff --git a/include/linux/nfs_idmap.h b/include/linux/nfs_idmap.h index 717fa5019e75..7eed2012d288 100644 --- a/include/linux/nfs_idmap.h +++ b/include/linux/nfs_idmap.h @@ -69,8 +69,19 @@ struct nfs_server; struct nfs_fattr; struct nfs4_string; +#ifdef CONFIG_NFS_V4 int nfs_idmap_init(void); void nfs_idmap_quit(void); +#else +static inline int nfs_idmap_init(void) +{ + return 0; +} + +static inline void nfs_idmap_quit(void) +{} +#endif + int nfs_idmap_new(struct nfs_client *); void nfs_idmap_delete(struct nfs_client *); -- cgit v1.2.3 From 2d2f24add1ff903ff8e0ce61c5c05635cc636985 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sun, 4 Mar 2012 18:13:57 -0500 Subject: NFSv4: Simplify the struct nfs4_stateid Replace the union with the common struct stateid4 as defined in both RFC3530 and RFC5661. This makes it easier to access the sequence id, which will again make implementing support for parallel OPEN calls easier. Signed-off-by: Trond Myklebust --- fs/nfs/callback_xdr.c | 4 ++-- fs/nfs/nfs4_fs.h | 4 ++-- fs/nfs/nfs4proc.c | 7 +++---- fs/nfs/nfs4state.c | 4 ++-- fs/nfs/nfs4xdr.c | 6 +++--- fs/nfs/pnfs.c | 10 +++++----- include/linux/nfs4.h | 7 ++----- 7 files changed, 19 insertions(+), 23 deletions(-) (limited to 'include/linux') diff --git a/fs/nfs/callback_xdr.c b/fs/nfs/callback_xdr.c index 5466829c7e77..fd6cfdb917da 100644 --- a/fs/nfs/callback_xdr.c +++ b/fs/nfs/callback_xdr.c @@ -138,10 +138,10 @@ static __be32 decode_stateid(struct xdr_stream *xdr, nfs4_stateid *stateid) { __be32 *p; - p = read_buf(xdr, 16); + p = read_buf(xdr, NFS4_STATEID_SIZE); if (unlikely(p == NULL)) return htonl(NFS4ERR_RESOURCE); - memcpy(stateid->data, p, 16); + memcpy(stateid, p, NFS4_STATEID_SIZE); return 0; } diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index 1c54ef3146d4..16373df96f90 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -351,12 +351,12 @@ extern struct svc_version nfs4_callback_version4; static inline void nfs4_stateid_copy(nfs4_stateid *dst, const nfs4_stateid *src) { - memcpy(dst->data, src->data, sizeof(dst->data)); + memcpy(dst, src, sizeof(*dst)); } static inline bool nfs4_stateid_match(const nfs4_stateid *dst, const nfs4_stateid *src) { - return memcmp(dst->data, src->data, sizeof(dst->data)) == 0; + return memcmp(dst, src, sizeof(*dst)) == 0; } #else diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index ce0ad81dd466..e0e35288361c 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -6271,13 +6271,12 @@ static int nfs41_free_stateid(struct nfs_server *server, nfs4_stateid *stateid) static bool nfs41_match_stateid(const nfs4_stateid *s1, const nfs4_stateid *s2) { - if (memcmp(s1->stateid.other, s2->stateid.other, - sizeof(s1->stateid.other)) != 0) + if (memcmp(s1->other, s2->other, sizeof(s1->other)) != 0) return false; - if (s1->stateid.seqid == s2->stateid.seqid) + if (s1->seqid == s2->seqid) return true; - if (s1->stateid.seqid == 0 || s2->stateid.seqid == 0) + if (s1->seqid == 0 || s2->seqid == 0) return true; return false; diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 55c8a81cd6fb..1dad5c53c7fa 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -1240,8 +1240,8 @@ restart: * Open state on this file cannot be recovered * All we can do is revert to using the zero stateid. */ - memset(state->stateid.data, 0, - sizeof(state->stateid.data)); + memset(&state->stateid, 0, + sizeof(state->stateid)); /* Mark the file as being 'closed' */ state->state = 0; break; diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 38736dca1b18..76ef98632839 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -930,7 +930,7 @@ static void encode_nops(struct compound_hdr *hdr) static void encode_nfs4_stateid(struct xdr_stream *xdr, const nfs4_stateid *stateid) { - encode_opaque_fixed(xdr, stateid->data, NFS4_STATEID_SIZE); + encode_opaque_fixed(xdr, stateid, NFS4_STATEID_SIZE); } static void encode_nfs4_verifier(struct xdr_stream *xdr, const nfs4_verifier *verf) @@ -1548,7 +1548,7 @@ static void encode_open_stateid(struct xdr_stream *xdr, const struct nfs_open_co if (ctx->state != NULL) { nfs4_select_rw_stateid(&stateid, ctx->state, l_ctx->lockowner, l_ctx->pid); if (zero_seqid) - stateid.stateid.seqid = 0; + stateid.seqid = 0; encode_nfs4_stateid(xdr, &stateid); } else encode_nfs4_stateid(xdr, &zero_stateid); @@ -4237,7 +4237,7 @@ static int decode_opaque_fixed(struct xdr_stream *xdr, void *buf, size_t len) static int decode_stateid(struct xdr_stream *xdr, nfs4_stateid *stateid) { - return decode_opaque_fixed(xdr, stateid->data, NFS4_STATEID_SIZE); + return decode_opaque_fixed(xdr, stateid, NFS4_STATEID_SIZE); } static int decode_close(struct xdr_stream *xdr, struct nfs_closeres *res) diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c index c190e9c2e3d2..6f1c1e3d12bc 100644 --- a/fs/nfs/pnfs.c +++ b/fs/nfs/pnfs.c @@ -496,12 +496,12 @@ pnfs_set_layout_stateid(struct pnfs_layout_hdr *lo, const nfs4_stateid *new, { u32 oldseq, newseq; - oldseq = be32_to_cpu(lo->plh_stateid.stateid.seqid); - newseq = be32_to_cpu(new->stateid.seqid); + oldseq = be32_to_cpu(lo->plh_stateid.seqid); + newseq = be32_to_cpu(new->seqid); if ((int)(newseq - oldseq) > 0) { nfs4_stateid_copy(&lo->plh_stateid, new); if (update_barrier) { - u32 new_barrier = be32_to_cpu(new->stateid.seqid); + u32 new_barrier = be32_to_cpu(new->seqid); if ((int)(new_barrier - lo->plh_barrier)) lo->plh_barrier = new_barrier; @@ -525,7 +525,7 @@ pnfs_layoutgets_blocked(struct pnfs_layout_hdr *lo, nfs4_stateid *stateid, int lget) { if ((stateid) && - (int)(lo->plh_barrier - be32_to_cpu(stateid->stateid.seqid)) >= 0) + (int)(lo->plh_barrier - be32_to_cpu(stateid->seqid)) >= 0) return true; return lo->plh_block_lgets || test_bit(NFS_LAYOUT_DESTROYED, &lo->plh_flags) || @@ -759,7 +759,7 @@ bool pnfs_roc_drain(struct inode *ino, u32 *barrier) } if (!found) { struct pnfs_layout_hdr *lo = nfsi->layout; - u32 current_seqid = be32_to_cpu(lo->plh_stateid.stateid.seqid); + u32 current_seqid = be32_to_cpu(lo->plh_stateid.seqid); /* Since close does not return a layout stateid for use as * a barrier, we choose the worst-case barrier. diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h index 32345c2805c0..834df8bf08b6 100644 --- a/include/linux/nfs4.h +++ b/include/linux/nfs4.h @@ -183,15 +183,12 @@ struct nfs4_acl { typedef struct { char data[NFS4_VERIFIER_SIZE]; } nfs4_verifier; -struct nfs41_stateid { +struct nfs_stateid4 { __be32 seqid; char other[NFS4_STATEID_OTHER_SIZE]; } __attribute__ ((packed)); -typedef union { - char data[NFS4_STATEID_SIZE]; - struct nfs41_stateid stateid; -} nfs4_stateid; +typedef struct nfs_stateid4 nfs4_stateid; enum nfs_opnum4 { OP_ACCESS = 3, -- cgit v1.2.3 From fd2c15ec1dd3c2fdfc6ff03bb9644da9d530e3b9 Mon Sep 17 00:00:00 2001 From: Ohad Ben-Cohen Date: Wed, 1 Feb 2012 21:56:16 +0200 Subject: remoteproc: resource table overhaul The resource table is an array of 'struct fw_resource' members, where each resource entry is expressed as a single member of that array. This approach got us this far, but it has a few drawbacks: 1. Different resource entries end up overloading the same members of 'struct fw_resource' with different meanings. The resulting code is error prone and hard to read and maintain. 2. It's impossible to extend 'struct fw_resource' without breaking the existing firmware images (and we already want to: we can't introduce the new virito device resource entry with the current scheme). 3. It doesn't scale: 'struct fw_resource' must be as big as the largest resource entry type. As a result, smaller resource entries end up utilizing only small part of it. This is fixed by defining a dedicated structure for every resource type, and then converting the resource table to a list of type-value members. Instead of a rigid array of homogeneous structs, the resource table is turned into a collection of heterogeneous structures. This way: 1. Resource entries consume exactly the amount of bytes they need. 2. It's easy to extend: just create a new resource entry structure, and assign it a new type. 3. The code is easier to read and maintain: the structures' members names are meaningful. While we're at it, this patch has several other resource table changes: 1. The resource table gains a simple header which contains the number of entries in the table and their offsets within the table. This makes the parsing code simpler and easier to read. 2. A version member is added to the resource table. Should we change the format again, we'll bump up this version to prevent breakage with existing firmware images. 3. The VRING and VIRTIO_DEV resource entries are combined to a single VDEV entry. This paves the way to supporting multiple VDEV entries. 4. Since we don't really support 64-bit rprocs yet, convert two stray u64 members to u32. Signed-off-by: Ohad Ben-Cohen Cc: Brian Swetland Cc: Iliyan Malchev Cc: Arnd Bergmann Cc: Grant Likely Cc: Rusty Russell Cc: Mark Grosen Cc: John Williams Cc: Michal Simek Cc: Loic PALLARDY Cc: Ludovic BARRE Cc: Omar Ramirez Luna Cc: Guzman Lugo Fernando Cc: Anna Suman Cc: Clark Rob Cc: Stephen Boyd Cc: Saravana Kannan Cc: David Brown Cc: Kieran Bingham Cc: Tony Lindgren --- Documentation/remoteproc.txt | 127 +++++++-------- drivers/remoteproc/remoteproc_core.c | 306 ++++++++++++++++++++++++----------- include/linux/remoteproc.h | 289 ++++++++++++++++++++++++++------- 3 files changed, 505 insertions(+), 217 deletions(-) (limited to 'include/linux') diff --git a/Documentation/remoteproc.txt b/Documentation/remoteproc.txt index 23ff7349ffe7..07057cacfeae 100644 --- a/Documentation/remoteproc.txt +++ b/Documentation/remoteproc.txt @@ -221,43 +221,52 @@ resource entries that publish the existence of supported features or configurations by the remote processor, such as trace buffers and supported virtio devices (and their configurations). -Currently the resource table is just an array of: +The resource table begins with this header: /** - * struct fw_resource - describes an entry from the resource section + * struct resource_table - firmware resource table header + * @ver: version number + * @num: number of resource entries + * @reserved: reserved (must be zero) + * @offset: array of offsets pointing at the various resource entries + * + * The header of the resource table, as expressed by this structure, + * contains a version number (should we need to change this format in the + * future), the number of available resource entries, and their offsets + * in the table. + */ +struct resource_table { + u32 ver; + u32 num; + u32 reserved[2]; + u32 offset[0]; +} __packed; + +Immediately following this header are the resource entries themselves, +each of which begins with the following resource entry header: + +/** + * struct fw_rsc_hdr - firmware resource entry header * @type: resource type - * @id: index number of the resource - * @da: device address of the resource - * @pa: physical address of the resource - * @len: size, in bytes, of the resource - * @flags: properties of the resource, e.g. iommu protection required - * @reserved: must be 0 atm - * @name: name of resource + * @data: resource data + * + * Every resource entry begins with a 'struct fw_rsc_hdr' header providing + * its @type. The content of the entry itself will immediately follow + * this header, and it should be parsed according to the resource type. */ -struct fw_resource { +struct fw_rsc_hdr { u32 type; - u32 id; - u64 da; - u64 pa; - u32 len; - u32 flags; - u8 reserved[16]; - u8 name[48]; + u8 data[0]; } __packed; Some resources entries are mere announcements, where the host is informed of specific remoteproc configuration. Other entries require the host to -do something (e.g. reserve a requested resource) and possibly also reply -by overwriting a member inside 'struct fw_resource' with info about the -allocated resource. - -Different resource entries use different members of this struct, -with different meanings. This is pretty limiting and error-prone, -so the plan is to move to variable-length TLV-based resource entries, -where each resource will begin with a type and length fields, followed by -its own specific structure. +do something (e.g. allocate a system resource). Sometimes a negotiation +is expected, where the firmware requests a resource, and once allocated, +the host should provide back its details (e.g. address of an allocated +memory region). -Here are the resource types that are currently being used: +Here are the various resource types that are currently supported: /** * enum fw_resource_type - types of resource entries @@ -266,59 +275,45 @@ Here are the resource types that are currently being used: * memory region. * @RSC_DEVMEM: request to iommu_map a memory-based peripheral. * @RSC_TRACE: announces the availability of a trace buffer into which - * the remote processor will be writing logs. In this case, - * 'da' indicates the device address where logs are written to, - * and 'len' is the size of the trace buffer. - * @RSC_VRING: request for allocation of a virtio vring (address should - * be indicated in 'da', and 'len' should contain the number - * of buffers supported by the vring). - * @RSC_VIRTIO_DEV: announces support for a virtio device, and serves as - * the virtio header. 'da' contains the virtio device - * features, 'pa' holds the virtio guest features (host - * will write them here after they're negotiated), 'len' - * holds the virtio status, and 'flags' holds the virtio - * device id (currently only VIRTIO_ID_RPMSG is supported). + * the remote processor will be writing logs. + * @RSC_VDEV: declare support for a virtio device, and serve as its + * virtio header. + * @RSC_LAST: just keep this one at the end + * + * Please note that these values are used as indices to the rproc_handle_rsc + * lookup table, so please keep them sane. Moreover, @RSC_LAST is used to + * check the validity of an index before the lookup table is accessed, so + * please update it as needed. */ enum fw_resource_type { RSC_CARVEOUT = 0, RSC_DEVMEM = 1, RSC_TRACE = 2, - RSC_VRING = 3, - RSC_VIRTIO_DEV = 4, - RSC_VIRTIO_CFG = 5, + RSC_VDEV = 3, + RSC_LAST = 4, }; -Most of the resource entries share the basic idea of address/length -negotiation with the host: the firmware usually asks for memory -of size 'len' bytes, and the host needs to allocate it and provide -the device/physical address (when relevant) in 'da'/'pa' respectively. - -If the firmware is compiled with hard coded device addresses, and -can't handle dynamically allocated 'da' values, then the 'da' field -will contain the expected device addresses (today we actually only support -this scheme, as there aren't yet any use cases for dynamically allocated -device addresses). +For more details regarding a specific resource type, please see its +dedicated structure in include/linux/remoteproc.h. We also expect that platform-specific resource entries will show up -at some point. When that happens, we could easily add a new RSC_PLAFORM +at some point. When that happens, we could easily add a new RSC_PLATFORM type, and hand those resources to the platform-specific rproc driver to handle. 7. Virtio and remoteproc The firmware should provide remoteproc information about virtio devices -that it supports, and their configurations: a RSC_VIRTIO_DEV resource entry -should specify the virtio device id, and subsequent RSC_VRING resource entries -should indicate the vring size (i.e. how many buffers do they support) and -where should they be mapped (i.e. which device address). Note: the alignment -between the consumer and producer parts of the vring is assumed to be 4096. - -At this point we only support a single virtio rpmsg device per remote -processor, but the plan is to remove this limitation. In addition, once we -move to TLV-based resource table, the plan is to have a single RSC_VIRTIO -entry per supported virtio device, which will include the virtio header, -the vrings information and the virtio config space. - -Of course, RSC_VIRTIO resource entries are only good enough for static +that it supports, and their configurations: a RSC_VDEV resource entry +should specify the virtio device id (as in virtio_ids.h), virtio features, +virtio config space, vrings information, etc. + +When a new remote processor is registered, the remoteproc framework +will look for its resource table and will register the virtio devices +it supports. A firmware may support any number of virtio devices, and +of any type (a single remote processor can also easily support several +rpmsg virtio devices this way, if desired). + +Of course, RSC_VDEV resource entries are only good enough for static allocation of virtio devices. Dynamic allocations will also be made possible using the rpmsg bus (similar to how we already do dynamic allocations of rpmsg channels; read more about it in rpmsg.txt). diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c index 8990c51c16f0..10348451c6c9 100644 --- a/drivers/remoteproc/remoteproc_core.c +++ b/drivers/remoteproc/remoteproc_core.c @@ -63,9 +63,8 @@ static void klist_rproc_put(struct klist_node *n); static DEFINE_KLIST(rprocs, klist_rproc_get, klist_rproc_put); typedef int (*rproc_handle_resources_t)(struct rproc *rproc, - struct fw_resource *rsc, int len); -typedef int (*rproc_handle_resource_t)(struct rproc *rproc, - struct fw_resource *rsc); + struct resource_table *table, int len); +typedef int (*rproc_handle_resource_t)(struct rproc *rproc, void *, int avail); /* * This is the IOMMU fault handler we register with the IOMMU API @@ -281,9 +280,10 @@ rproc_load_segments(struct rproc *rproc, const u8 *elf_data, size_t len) } /** - * rproc_handle_virtio_hdr() - handle a virtio header resource + * rproc_handle_early_vdev() - early handle a virtio header resource * @rproc: the remote processor * @rsc: the resource descriptor + * @avail: size of available data (for sanity checking the image) * * The existence of this virtio hdr resource entry means that the firmware * of this @rproc supports this virtio device. @@ -291,37 +291,32 @@ rproc_load_segments(struct rproc *rproc, const u8 *elf_data, size_t len) * Currently we support only a single virtio device of type VIRTIO_ID_RPMSG, * but the plan is to remove this limitation and support any number * of virtio devices (and of any type). We'll also add support for dynamically - * adding (and removing) virtio devices over the rpmsg bus, but small + * adding (and removing) virtio devices over the rpmsg bus, but simple * firmwares that doesn't want to get involved with rpmsg will be able - * to simple use the resource table for this. - * - * At this point this virtio header entry is rather simple: it just - * announces the virtio device id and the supported virtio device features. - * The plan though is to extend this to include the vring information and - * the virtio config space, too (but first, some resource table overhaul - * is needed: move from fixed-sized to variable-length TLV entries). - * - * For now, the 'flags' member of the resource entry contains the virtio - * device id, the 'da' member contains the device features, and 'pa' is - * where we need to store the guest features once negotiation completes. - * As usual, the 'id' member of this resource contains the index of this - * resource type (i.e. is this the first virtio hdr entry, the 2nd, ...). + * to simply use the resource table for this. * * Returns 0 on success, or an appropriate error code otherwise */ -static int rproc_handle_virtio_hdr(struct rproc *rproc, struct fw_resource *rsc) +static int rproc_handle_early_vdev(struct rproc *rproc, struct fw_rsc_vdev *rsc, + int avail) { struct rproc_vdev *rvdev; + /* make sure resource isn't truncated */ + if (sizeof(*rsc) > avail) { + dev_err(rproc->dev, "vdev rsc is truncated\n"); + return -EINVAL; + } + /* we only support VIRTIO_ID_RPMSG devices for now */ - if (rsc->flags != VIRTIO_ID_RPMSG) { - dev_warn(rproc->dev, "unsupported vdev: %d\n", rsc->flags); + if (rsc->id != VIRTIO_ID_RPMSG) { + dev_warn(rproc->dev, "unsupported vdev: %d\n", rsc->id); return -EINVAL; } /* we only support a single vdev per rproc for now */ - if (rsc->id || rproc->rvdev) { - dev_warn(rproc->dev, "redundant vdev entry: %s\n", rsc->name); + if (rproc->rvdev) { + dev_warn(rproc->dev, "redundant vdev entry\n"); return -EINVAL; } @@ -330,7 +325,7 @@ static int rproc_handle_virtio_hdr(struct rproc *rproc, struct fw_resource *rsc) return -ENOMEM; /* remember the device features */ - rvdev->dfeatures = rsc->da; + rvdev->dfeatures = rsc->dfeatures; rproc->rvdev = rvdev; rvdev->rproc = rproc; @@ -339,9 +334,10 @@ static int rproc_handle_virtio_hdr(struct rproc *rproc, struct fw_resource *rsc) } /** - * rproc_handle_vring() - handle a vring fw resource + * rproc_handle_vdev() - handle a vdev fw resource * @rproc: the remote processor * @rsc: the vring resource descriptor + * @avail: size of available data (for sanity checking the image) * * This resource entry requires allocation of non-cacheable memory * for a virtio vring. Currently we only support two vrings per remote @@ -360,57 +356,82 @@ static int rproc_handle_virtio_hdr(struct rproc *rproc, struct fw_resource *rsc) * * Returns 0 on success, or an appropriate error code otherwise */ -static int rproc_handle_vring(struct rproc *rproc, struct fw_resource *rsc) +static int rproc_handle_vdev(struct rproc *rproc, struct fw_rsc_vdev *rsc, + int avail) { struct device *dev = rproc->dev; struct rproc_vdev *rvdev = rproc->rvdev; - dma_addr_t dma; - int size, id = rsc->id; - void *va; + int i; - /* no vdev is in place ? */ - if (!rvdev) { - dev_err(dev, "vring requested without a virtio dev entry\n"); + /* make sure resource isn't truncated */ + if (sizeof(*rsc) + rsc->num_of_vrings * sizeof(struct fw_rsc_vdev_vring) + + rsc->config_len > avail) { + dev_err(rproc->dev, "vdev rsc is truncated\n"); return -EINVAL; } - /* the firmware must provide the expected queue size */ - if (!rsc->len) { - dev_err(dev, "missing expected queue size\n"); + /* make sure reserved bytes are zeroes */ + if (rsc->reserved[0] || rsc->reserved[1]) { + dev_err(dev, "vdev rsc has non zero reserved bytes\n"); return -EINVAL; } - /* we currently support two vrings per rproc (for rx and tx) */ - if (id >= ARRAY_SIZE(rvdev->vring)) { - dev_err(dev, "%s: invalid vring id %d\n", rsc->name, id); + dev_dbg(dev, "vdev rsc: id %d, dfeatures %x, cfg len %d, %d vrings\n", + rsc->id, rsc->dfeatures, rsc->config_len, rsc->num_of_vrings); + + /* no vdev is in place ? */ + if (!rvdev) { + dev_err(dev, "vring requested without a virtio dev entry\n"); return -EINVAL; } - /* have we already allocated this vring id ? */ - if (rvdev->vring[id].len) { - dev_err(dev, "%s: duplicated id %d\n", rsc->name, id); + /* we currently support two vrings per rproc (for rx and tx) */ + if (rsc->num_of_vrings != ARRAY_SIZE(rvdev->vring)) { + dev_err(dev, "too many vrings: %d\n", rsc->num_of_vrings); return -EINVAL; } - /* actual size of vring (in bytes) */ - size = PAGE_ALIGN(vring_size(rsc->len, AMP_VRING_ALIGN)); + /* initialize the vrings */ + for (i = 0; i < rsc->num_of_vrings; i++) { + struct fw_rsc_vdev_vring *vring = &rsc->vring[i]; + dma_addr_t dma; + int size; + void *va; + + /* make sure reserved bytes are zeroes */ + if (vring->reserved) { + dev_err(dev, "vring rsc has non zero reserved bytes\n"); + return -EINVAL; + } - /* - * Allocate non-cacheable memory for the vring. In the future - * this call will also configure the IOMMU for us - */ - va = dma_alloc_coherent(dev, size, &dma, GFP_KERNEL); - if (!va) { - dev_err(dev, "dma_alloc_coherent failed\n"); - return -ENOMEM; - } + /* the firmware must provide the expected queue size */ + if (!vring->num) { + dev_err(dev, "missing expected queue size\n"); + /* potential cleanups are taken care of later on */ + return -EINVAL; + } - dev_dbg(dev, "vring%d: va %p dma %x qsz %d ring size %x\n", id, va, - dma, rsc->len, size); + /* actual size of vring (in bytes) */ + size = PAGE_ALIGN(vring_size(vring->num, AMP_VRING_ALIGN)); - rvdev->vring[id].len = rsc->len; - rvdev->vring[id].va = va; - rvdev->vring[id].dma = dma; + /* + * Allocate non-cacheable memory for the vring. In the future + * this call will also configure the IOMMU for us + */ + va = dma_alloc_coherent(dev, size, &dma, GFP_KERNEL); + if (!va) { + dev_err(dev, "dma_alloc_coherent failed\n"); + /* potential cleanups are taken care of later on */ + return -EINVAL; + } + + dev_dbg(dev, "vring%d: va %p dma %x qsz %d ring size %x\n", i, + va, dma, vring->num, size); + + rvdev->vring[i].len = vring->num; + rvdev->vring[i].va = va; + rvdev->vring[i].dma = dma; + } return 0; } @@ -419,6 +440,7 @@ static int rproc_handle_vring(struct rproc *rproc, struct fw_resource *rsc) * rproc_handle_trace() - handle a shared trace buffer resource * @rproc: the remote processor * @rsc: the trace resource descriptor + * @avail: size of available data (for sanity checking the image) * * In case the remote processor dumps trace logs into memory, * export it via debugfs. @@ -430,13 +452,25 @@ static int rproc_handle_vring(struct rproc *rproc, struct fw_resource *rsc) * * Returns 0 on success, or an appropriate error code otherwise */ -static int rproc_handle_trace(struct rproc *rproc, struct fw_resource *rsc) +static int rproc_handle_trace(struct rproc *rproc, struct fw_rsc_trace *rsc, + int avail) { struct rproc_mem_entry *trace; struct device *dev = rproc->dev; void *ptr; char name[15]; + if (sizeof(*rsc) > avail) { + dev_err(rproc->dev, "trace rsc is truncated\n"); + return -EINVAL; + } + + /* make sure reserved bytes are zeroes */ + if (rsc->reserved) { + dev_err(dev, "trace rsc has non zero reserved bytes\n"); + return -EINVAL; + } + /* what's the kernel address of this resource ? */ ptr = rproc_da_to_va(rproc, rsc->da, rsc->len); if (!ptr) { @@ -469,7 +503,7 @@ static int rproc_handle_trace(struct rproc *rproc, struct fw_resource *rsc) rproc->num_traces++; - dev_dbg(dev, "%s added: va %p, da 0x%llx, len 0x%x\n", name, ptr, + dev_dbg(dev, "%s added: va %p, da 0x%x, len 0x%x\n", name, ptr, rsc->da, rsc->len); return 0; @@ -479,6 +513,7 @@ static int rproc_handle_trace(struct rproc *rproc, struct fw_resource *rsc) * rproc_handle_devmem() - handle devmem resource entry * @rproc: remote processor handle * @rsc: the devmem resource entry + * @avail: size of available data (for sanity checking the image) * * Remote processors commonly need to access certain on-chip peripherals. * @@ -499,7 +534,8 @@ static int rproc_handle_trace(struct rproc *rproc, struct fw_resource *rsc) * and not allow firmwares to request access to physical addresses that * are outside those ranges. */ -static int rproc_handle_devmem(struct rproc *rproc, struct fw_resource *rsc) +static int rproc_handle_devmem(struct rproc *rproc, struct fw_rsc_devmem *rsc, + int avail) { struct rproc_mem_entry *mapping; int ret; @@ -508,6 +544,17 @@ static int rproc_handle_devmem(struct rproc *rproc, struct fw_resource *rsc) if (!rproc->domain) return -EINVAL; + if (sizeof(*rsc) > avail) { + dev_err(rproc->dev, "devmem rsc is truncated\n"); + return -EINVAL; + } + + /* make sure reserved bytes are zeroes */ + if (rsc->reserved) { + dev_err(rproc->dev, "devmem rsc has non zero reserved bytes\n"); + return -EINVAL; + } + mapping = kzalloc(sizeof(*mapping), GFP_KERNEL); if (!mapping) { dev_err(rproc->dev, "kzalloc mapping failed\n"); @@ -531,7 +578,7 @@ static int rproc_handle_devmem(struct rproc *rproc, struct fw_resource *rsc) mapping->len = rsc->len; list_add_tail(&mapping->node, &rproc->mappings); - dev_dbg(rproc->dev, "mapped devmem pa 0x%llx, da 0x%llx, len 0x%x\n", + dev_dbg(rproc->dev, "mapped devmem pa 0x%x, da 0x%x, len 0x%x\n", rsc->pa, rsc->da, rsc->len); return 0; @@ -545,6 +592,7 @@ out: * rproc_handle_carveout() - handle phys contig memory allocation requests * @rproc: rproc handle * @rsc: the resource entry + * @avail: size of available data (for image validation) * * This function will handle firmware requests for allocation of physically * contiguous memory regions. @@ -558,7 +606,8 @@ out: * needed to map it (in case @rproc is using an IOMMU). Reducing the TLB * pressure is important; it may have a substantial impact on performance. */ -static int rproc_handle_carveout(struct rproc *rproc, struct fw_resource *rsc) +static int rproc_handle_carveout(struct rproc *rproc, + struct fw_rsc_carveout *rsc, int avail) { struct rproc_mem_entry *carveout, *mapping; struct device *dev = rproc->dev; @@ -566,6 +615,20 @@ static int rproc_handle_carveout(struct rproc *rproc, struct fw_resource *rsc) void *va; int ret; + if (sizeof(*rsc) > avail) { + dev_err(rproc->dev, "carveout rsc is truncated\n"); + return -EINVAL; + } + + /* make sure reserved bytes are zeroes */ + if (rsc->reserved) { + dev_err(dev, "carveout rsc has non zero reserved bytes\n"); + return -EINVAL; + } + + dev_dbg(dev, "carveout rsc: da %x, pa %x, len %x, flags %x\n", + rsc->da, rsc->pa, rsc->len, rsc->flags); + mapping = kzalloc(sizeof(*mapping), GFP_KERNEL); if (!mapping) { dev_err(dev, "kzalloc mapping failed\n"); @@ -624,7 +687,7 @@ static int rproc_handle_carveout(struct rproc *rproc, struct fw_resource *rsc) mapping->len = rsc->len; list_add_tail(&mapping->node, &rproc->mappings); - dev_dbg(dev, "carveout mapped 0x%llx to 0x%x\n", rsc->da, dma); + dev_dbg(dev, "carveout mapped 0x%x to 0x%x\n", rsc->da, dma); /* * Some remote processors might need to know the pa @@ -665,36 +728,44 @@ free_mapping: * enum fw_resource_type. */ static rproc_handle_resource_t rproc_handle_rsc[] = { - [RSC_CARVEOUT] = rproc_handle_carveout, - [RSC_DEVMEM] = rproc_handle_devmem, - [RSC_TRACE] = rproc_handle_trace, - [RSC_VRING] = rproc_handle_vring, - [RSC_VIRTIO_DEV] = NULL, /* handled early upon registration */ + [RSC_CARVEOUT] = (rproc_handle_resource_t)rproc_handle_carveout, + [RSC_DEVMEM] = (rproc_handle_resource_t)rproc_handle_devmem, + [RSC_TRACE] = (rproc_handle_resource_t)rproc_handle_trace, + [RSC_VDEV] = (rproc_handle_resource_t)rproc_handle_vdev, }; /* handle firmware resource entries before booting the remote processor */ static int -rproc_handle_boot_rsc(struct rproc *rproc, struct fw_resource *rsc, int len) +rproc_handle_boot_rsc(struct rproc *rproc, struct resource_table *table, int len) { struct device *dev = rproc->dev; rproc_handle_resource_t handler; - int ret = 0; + int ret = 0, i; + + for (i = 0; i < table->num; i++) { + int offset = table->offset[i]; + struct fw_rsc_hdr *hdr = (void *)table + offset; + int avail = len - offset - sizeof(*hdr); + void *rsc = (void *)hdr + sizeof(*hdr); + + /* make sure table isn't truncated */ + if (avail < 0) { + dev_err(dev, "rsc table is truncated\n"); + return -EINVAL; + } - for (; len >= sizeof(*rsc); rsc++, len -= sizeof(*rsc)) { - dev_dbg(dev, "rsc: type %d, da 0x%llx, pa 0x%llx, len 0x%x, " - "id %d, name %s, flags %x\n", rsc->type, rsc->da, - rsc->pa, rsc->len, rsc->id, rsc->name, rsc->flags); + dev_dbg(dev, "rsc: type %d\n", hdr->type); - if (rsc->type >= RSC_LAST) { - dev_warn(dev, "unsupported resource %d\n", rsc->type); + if (hdr->type >= RSC_LAST) { + dev_warn(dev, "unsupported resource %d\n", hdr->type); continue; } - handler = rproc_handle_rsc[rsc->type]; + handler = rproc_handle_rsc[hdr->type]; if (!handler) continue; - ret = handler(rproc, rsc); + ret = handler(rproc, rsc, avail); if (ret) break; } @@ -704,18 +775,31 @@ rproc_handle_boot_rsc(struct rproc *rproc, struct fw_resource *rsc, int len) /* handle firmware resource entries while registering the remote processor */ static int -rproc_handle_virtio_rsc(struct rproc *rproc, struct fw_resource *rsc, int len) +rproc_handle_virtio_rsc(struct rproc *rproc, struct resource_table *table, int len) { struct device *dev = rproc->dev; - int ret = -ENODEV; + int ret = 0, i; + + for (i = 0; i < table->num; i++) { + int offset = table->offset[i]; + struct fw_rsc_hdr *hdr = (void *)table + offset; + int avail = len - offset - sizeof(*hdr); - for (; len >= sizeof(*rsc); rsc++, len -= sizeof(*rsc)) - if (rsc->type == RSC_VIRTIO_DEV) { - dev_dbg(dev, "found vdev %d/%s features %llx\n", - rsc->flags, rsc->name, rsc->da); - ret = rproc_handle_virtio_hdr(rproc, rsc); + /* make sure table isn't truncated */ + if (avail < 0) { + dev_err(dev, "rsc table is truncated\n"); + return -EINVAL; + } + + dev_dbg(dev, "%s: rsc type %d\n", __func__, hdr->type); + + if (hdr->type == RSC_VDEV) { + struct fw_rsc_vdev *vrsc = + (struct fw_rsc_vdev *)hdr->data; + ret = rproc_handle_early_vdev(rproc, vrsc, avail); break; } + } return ret; } @@ -744,7 +828,9 @@ static int rproc_handle_resources(struct rproc *rproc, const u8 *elf_data, struct elf32_hdr *ehdr; struct elf32_shdr *shdr; const char *name_table; + struct device *dev = rproc->dev; int i, ret = -EINVAL; + struct resource_table *table; ehdr = (struct elf32_hdr *)elf_data; shdr = (struct elf32_shdr *)(elf_data + ehdr->e_shoff); @@ -752,21 +838,47 @@ static int rproc_handle_resources(struct rproc *rproc, const u8 *elf_data, /* look for the resource table and handle it */ for (i = 0; i < ehdr->e_shnum; i++, shdr++) { - if (!strcmp(name_table + shdr->sh_name, ".resource_table")) { - struct fw_resource *table = (struct fw_resource *) - (elf_data + shdr->sh_offset); + int size = shdr->sh_size; + int offset = shdr->sh_offset; - if (shdr->sh_offset + shdr->sh_size > len) { - dev_err(rproc->dev, - "truncated fw: need 0x%x avail 0x%x\n", - shdr->sh_offset + shdr->sh_size, len); - ret = -EINVAL; - } + if (strcmp(name_table + shdr->sh_name, ".resource_table")) + continue; - ret = handler(rproc, table, shdr->sh_size); + table = (struct resource_table *)(elf_data + offset); - break; + /* make sure we have the entire table */ + if (offset + size > len) { + dev_err(dev, "resource table truncated\n"); + return -EINVAL; + } + + /* make sure table has at least the header */ + if (sizeof(struct resource_table) > size) { + dev_err(dev, "header-less resource table\n"); + return -EINVAL; } + + /* we don't support any version beyond the first */ + if (table->ver != 1) { + dev_err(dev, "unsupported fw ver: %d\n", table->ver); + return -EINVAL; + } + + /* make sure reserved bytes are zeroes */ + if (table->reserved[0] || table->reserved[1]) { + dev_err(dev, "non zero reserved bytes\n"); + return -EINVAL; + } + + /* make sure the offsets array isn't truncated */ + if (table->num * sizeof(table->offset[0]) + + sizeof(struct resource_table) > size) { + dev_err(dev, "resource table incomplete\n"); + return -EINVAL; + } + + ret = handler(rproc, table, shdr->sh_size); + break; } return ret; @@ -980,7 +1092,7 @@ static void rproc_fw_config_virtio(const struct firmware *fw, void *context) if (rproc_fw_sanity_check(rproc, fw) < 0) goto out; - /* does the fw supports any virtio devices ? */ + /* does the fw support any virtio devices ? */ ret = rproc_handle_resources(rproc, fw->data, fw->size, rproc_handle_virtio_rsc); if (ret) { diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h index ada4cb063dfe..6040f831f626 100644 --- a/include/linux/remoteproc.h +++ b/include/linux/remoteproc.h @@ -50,39 +50,51 @@ #define AMP_VRING_ALIGN (4096) /** - * struct fw_resource - describes an entry from the resource section - * @type: resource type - * @id: index number of the resource - * @da: device address of the resource - * @pa: physical address of the resource - * @len: size, in bytes, of the resource - * @flags: properties of the resource, e.g. iommu protection required - * @reserved: must be 0 atm - * @name: name of resource + * struct resource_table - firmware resource table header + * @ver: version number + * @num: number of resource entries + * @reserved: reserved (must be zero) + * @offset: array of offsets pointing at the various resource entries * - * The remote processor firmware should contain a "resource table": - * array of 'struct fw_resource' entries. + * A resource table is essentially a list of system resources required + * by the remote processor. It may also include configuration entries. + * If needed, the remote processor firmware should contain this table + * as a dedicated ".resource_table" ELF section. * * Some resources entries are mere announcements, where the host is informed * of specific remoteproc configuration. Other entries require the host to - * do something (e.g. reserve a requested resource) and possibly also reply - * by overwriting a member inside 'struct fw_resource' with info about the - * allocated resource. - * - * Different resource entries use different members of this struct, - * with different meanings. This is pretty limiting and error-prone, - * so the plan is to move to variable-length TLV-based resource entries, - * where each resource type will have its own structure. + * do something (e.g. allocate a system resource). Sometimes a negotiation + * is expected, where the firmware requests a resource, and once allocated, + * the host should provide back its details (e.g. address of an allocated + * memory region). + * + * The header of the resource table, as expressed by this structure, + * contains a version number (should we need to change this format in the + * future), the number of available resource entries, and their offsets + * in the table. + * + * Immediately following this header are the resource entries themselves, + * each of which begins with a resource entry header (as described below). + */ +struct resource_table { + u32 ver; + u32 num; + u32 reserved[2]; + u32 offset[0]; +} __packed; + +/** + * struct fw_rsc_hdr - firmware resource entry header + * @type: resource type + * @data: resource data + * + * Every resource entry begins with a 'struct fw_rsc_hdr' header providing + * its @type. The content of the entry itself will immediately follow + * this header, and it should be parsed according to the resource type. */ -struct fw_resource { +struct fw_rsc_hdr { u32 type; - u32 id; - u64 da; - u64 pa; - u32 len; - u32 flags; - u8 reserved[16]; - u8 name[48]; + u8 data[0]; } __packed; /** @@ -92,30 +104,13 @@ struct fw_resource { * memory region. * @RSC_DEVMEM: request to iommu_map a memory-based peripheral. * @RSC_TRACE: announces the availability of a trace buffer into which - * the remote processor will be writing logs. In this case, - * 'da' indicates the device address where logs are written to, - * and 'len' is the size of the trace buffer. - * @RSC_VRING: request for allocation of a virtio vring (address should - * be indicated in 'da', and 'len' should contain the number - * of buffers supported by the vring). - * @RSC_VIRTIO_DEV: this entry declares about support for a virtio device, - * and serves as the virtio header. 'da' holds the - * the virtio device features, 'pa' holds the virtio guest - * features, 'len' holds the virtio status, and 'flags' holds - * the virtio id (currently only VIRTIO_ID_RPMSG is supported). + * the remote processor will be writing logs. + * @RSC_VDEV: declare support for a virtio device, and serve as its + * virtio header. * @RSC_LAST: just keep this one at the end * - * Most of the resource entries share the basic idea of address/length - * negotiation with the host: the firmware usually asks (on behalf of the - * remote processor that will soon be booted with it) for memory - * of size 'len' bytes, and the host needs to allocate it and provide - * the device/physical address (when relevant) in 'da'/'pa' respectively. - * - * If the firmware is compiled with hard coded device addresses, and - * can't handle dynamically allocated 'da' values, then the 'da' field - * will contain the expected device addresses (today we actually only support - * this scheme, as there aren't yet any use cases for dynamically allocated - * device addresses). + * For more details regarding a specific resource type, please see its + * dedicated structure below. * * Please note that these values are used as indices to the rproc_handle_rsc * lookup table, so please keep them sane. Moreover, @RSC_LAST is used to @@ -126,11 +121,197 @@ enum fw_resource_type { RSC_CARVEOUT = 0, RSC_DEVMEM = 1, RSC_TRACE = 2, - RSC_VRING = 3, - RSC_VIRTIO_DEV = 4, - RSC_LAST = 5, + RSC_VDEV = 3, + RSC_LAST = 4, }; +#define FW_RSC_ADDR_ANY (0xFFFFFFFFFFFFFFFF) + +/** + * struct fw_rsc_carveout - physically contiguous memory request + * @da: device address + * @pa: physical address + * @len: length (in bytes) + * @flags: iommu protection flags + * @reserved: reserved (must be zero) + * @name: human-readable name of the requested memory region + * + * This resource entry requests the host to allocate a physically contiguous + * memory region. + * + * These request entries should precede other firmware resource entries, + * as other entries might request placing other data objects inside + * these memory regions (e.g. data/code segments, trace resource entries, ...). + * + * Allocating memory this way helps utilizing the reserved physical memory + * (e.g. CMA) more efficiently, and also minimizes the number of TLB entries + * needed to map it (in case @rproc is using an IOMMU). Reducing the TLB + * pressure is important; it may have a substantial impact on performance. + * + * If the firmware is compiled with static addresses, then @da should specify + * the expected device address of this memory region. If @da is set to + * FW_RSC_ADDR_ANY, then the host will dynamically allocate it, and then + * overwrite @da with the dynamically allocated address. + * + * We will always use @da to negotiate the device addresses, even if it + * isn't using an iommu. In that case, though, it will obviously contain + * physical addresses. + * + * Some remote processors needs to know the allocated physical address + * even if they do use an iommu. This is needed, e.g., if they control + * hardware accelerators which access the physical memory directly (this + * is the case with OMAP4 for instance). In that case, the host will + * overwrite @pa with the dynamically allocated physical address. + * Generally we don't want to expose physical addresses if we don't have to + * (remote processors are generally _not_ trusted), so we might want to + * change this to happen _only_ when explicitly required by the hardware. + * + * @flags is used to provide IOMMU protection flags, and @name should + * (optionally) contain a human readable name of this carveout region + * (mainly for debugging purposes). + */ +struct fw_rsc_carveout { + u32 da; + u32 pa; + u32 len; + u32 flags; + u32 reserved; + u8 name[32]; +} __packed; + +/** + * struct fw_rsc_devmem - iommu mapping request + * @da: device address + * @pa: physical address + * @len: length (in bytes) + * @flags: iommu protection flags + * @reserved: reserved (must be zero) + * @name: human-readable name of the requested region to be mapped + * + * This resource entry requests the host to iommu map a physically contiguous + * memory region. This is needed in case the remote processor requires + * access to certain memory-based peripherals; _never_ use it to access + * regular memory. + * + * This is obviously only needed if the remote processor is accessing memory + * via an iommu. + * + * @da should specify the required device address, @pa should specify + * the physical address we want to map, @len should specify the size of + * the mapping and @flags is the IOMMU protection flags. As always, @name may + * (optionally) contain a human readable name of this mapping (mainly for + * debugging purposes). + * + * Note: at this point we just "trust" those devmem entries to contain valid + * physical addresses, but this isn't safe and will be changed: eventually we + * want remoteproc implementations to provide us ranges of physical addresses + * the firmware is allowed to request, and not allow firmwares to request + * access to physical addresses that are outside those ranges. + */ +struct fw_rsc_devmem { + u32 da; + u32 pa; + u32 len; + u32 flags; + u32 reserved; + u8 name[32]; +} __packed; + +/** + * struct fw_rsc_trace - trace buffer declaration + * @da: device address + * @len: length (in bytes) + * @reserved: reserved (must be zero) + * @name: human-readable name of the trace buffer + * + * This resource entry provides the host information about a trace buffer + * into which the remote processor will write log messages. + * + * @da specifies the device address of the buffer, @len specifies + * its size, and @name may contain a human readable name of the trace buffer. + * + * After booting the remote processor, the trace buffers are exposed to the + * user via debugfs entries (called trace0, trace1, etc..). + */ +struct fw_rsc_trace { + u32 da; + u32 len; + u32 reserved; + u8 name[32]; +} __packed; + +/** + * struct fw_rsc_vdev_vring - vring descriptor entry + * @da: device address + * @align: the alignment between the consumer and producer parts of the vring + * @num: num of buffers supported by this vring (must be power of two) + * @notifyid is a unique rproc-wide notify index for this vring. This notify + * index is used when kicking a remote processor, to let it know that this + * vring is triggered. + * @reserved: reserved (must be zero) + * + * This descriptor is not a resource entry by itself; it is part of the + * vdev resource type (see below). + * + * Note that @da should either contain the device address where + * the remote processor is expecting the vring, or indicate that + * dynamically allocation of the vring's device address is supported. + */ +struct fw_rsc_vdev_vring { + u32 da; + u32 align; + u32 num; + u32 notifyid; + u32 reserved; +} __packed; + +/** + * struct fw_rsc_vdev - virtio device header + * @id: virtio device id (as in virtio_ids.h) + * @notifyid is a unique rproc-wide notify index for this vdev. This notify + * index is used when kicking a remote processor, to let it know that the + * status/features of this vdev have changes. + * @dfeatures specifies the virtio device features supported by the firmware + * @gfeatures is a place holder used by the host to write back the + * negotiated features that are supported by both sides. + * @config_len is the size of the virtio config space of this vdev. The config + * space lies in the resource table immediate after this vdev header. + * @status is a place holder where the host will indicate its virtio progress. + * @num_of_vrings indicates how many vrings are described in this vdev header + * @reserved: reserved (must be zero) + * @vring is an array of @num_of_vrings entries of 'struct fw_rsc_vdev_vring'. + * + * This resource is a virtio device header: it provides information about + * the vdev, and is then used by the host and its peer remote processors + * to negotiate and share certain virtio properties. + * + * By providing this resource entry, the firmware essentially asks remoteproc + * to statically allocate a vdev upon registration of the rproc (dynamic vdev + * allocation is not yet supported). + * + * Note: unlike virtualization systems, the term 'host' here means + * the Linux side which is running remoteproc to control the remote + * processors. We use the name 'gfeatures' to comply with virtio's terms, + * though there isn't really any virtualized guest OS here: it's the host + * which is responsible for negotiating the final features. + * Yeah, it's a bit confusing. + * + * Note: immediately following this structure is the virtio config space for + * this vdev (which is specific to the vdev; for more info, read the virtio + * spec). the size of the config space is specified by @config_len. + */ +struct fw_rsc_vdev { + u32 id; + u32 notifyid; + u32 dfeatures; + u32 gfeatures; + u32 config_len; + u8 status; + u8 num_of_vrings; + u8 reserved[2]; + struct fw_rsc_vdev_vring vring[0]; +} __packed; + /** * struct rproc_mem_entry - memory entry descriptor * @va: virtual address @@ -144,7 +325,7 @@ struct rproc_mem_entry { void *va; dma_addr_t dma; int len; - u64 da; + u32 da; void *priv; struct list_head node; }; @@ -226,7 +407,7 @@ struct rproc { struct list_head carveouts; struct list_head mappings; struct completion firmware_loading_complete; - u64 bootaddr; + u32 bootaddr; struct rproc_vdev *rvdev; }; -- cgit v1.2.3 From 7a186941626d19f668b08108db158379b32e6e02 Mon Sep 17 00:00:00 2001 From: Ohad Ben-Cohen Date: Mon, 13 Feb 2012 22:30:39 +0100 Subject: remoteproc: remove the single rpmsg vdev limitation Now that the resource table supports publishing a virtio device in a single resource entry, firmware images can start supporting more than a single vdev. This patch removes the single vdev limitation of the remoteproc framework so multi-vdev firmwares can be leveraged: VDEV resource entries are parsed when the rproc is registered, and as a result their vrings are set up and the virtio devices are registered (and they go away when the rproc goes away). Moreover, we no longer only support VIRTIO_ID_RPMSG vdevs; any virtio device type goes now. As a result, there's no more any rpmsg-specific APIs or code in remoteproc: it all becomes generic virtio handling. Signed-off-by: Ohad Ben-Cohen Cc: Brian Swetland Cc: Iliyan Malchev Cc: Arnd Bergmann Cc: Grant Likely Cc: Rusty Russell Cc: Mark Grosen Cc: John Williams Cc: Michal Simek Cc: Loic PALLARDY Cc: Ludovic BARRE Cc: Omar Ramirez Luna Cc: Guzman Lugo Fernando Cc: Anna Suman Cc: Clark Rob Cc: Stephen Boyd Cc: Saravana Kannan Cc: David Brown Cc: Kieran Bingham Cc: Tony Lindgren --- Documentation/remoteproc.txt | 9 +- drivers/remoteproc/remoteproc_core.c | 292 ++++++++++++++++--------------- drivers/remoteproc/remoteproc_internal.h | 6 +- drivers/remoteproc/remoteproc_virtio.c | 140 +++++++-------- include/linux/remoteproc.h | 41 ++++- 5 files changed, 260 insertions(+), 228 deletions(-) (limited to 'include/linux') diff --git a/Documentation/remoteproc.txt b/Documentation/remoteproc.txt index 07057cacfeae..70a048cd3fa3 100644 --- a/Documentation/remoteproc.txt +++ b/Documentation/remoteproc.txt @@ -20,6 +20,11 @@ platform-specific remoteproc drivers only need to provide a few low-level handlers, and then all rpmsg drivers will then just work (for more information about the virtio-based rpmsg bus and its drivers, please read Documentation/rpmsg.txt). +Registration of other types of virtio devices is now also possible. Firmwares +just need to publish what kind of virtio devices do they support, and then +remoteproc will add those devices. This makes it possible to reuse the +existing virtio drivers with remote processor backends at a minimal development +cost. 2. User API @@ -136,8 +141,6 @@ int dummy_rproc_example(struct rproc *my_rproc) If found, those virtio devices will be created and added, so as a result of registering this remote processor, additional virtio drivers might get probed. - Currently, though, we only support a single RPMSG virtio vdev per remote - processor. int rproc_unregister(struct rproc *rproc) - Unregister a remote processor, and decrement its refcount. @@ -174,7 +177,7 @@ struct rproc_ops { }; Every remoteproc implementation should at least provide the ->start and ->stop -handlers. If rpmsg functionality is also desired, then the ->kick handler +handlers. If rpmsg/virtio functionality is also desired, then the ->kick handler should be provided as well. The ->start() handler takes an rproc handle and should then power on the diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c index 10348451c6c9..ca02f128b435 100644 --- a/drivers/remoteproc/remoteproc_core.c +++ b/drivers/remoteproc/remoteproc_core.c @@ -52,8 +52,8 @@ static void klist_rproc_put(struct klist_node *n); * We need this in order to support name-based lookups (needed by the * rproc_get_by_name()). * - * That said, we don't use rproc_get_by_name() anymore within the rpmsg - * framework. The use cases that do require its existence should be + * That said, we don't use rproc_get_by_name() at this point. + * The use cases that do require its existence should be * scrutinized, and hopefully migrated to rproc_boot() using device-based * binding. * @@ -279,80 +279,112 @@ rproc_load_segments(struct rproc *rproc, const u8 *elf_data, size_t len) return ret; } -/** - * rproc_handle_early_vdev() - early handle a virtio header resource - * @rproc: the remote processor - * @rsc: the resource descriptor - * @avail: size of available data (for sanity checking the image) - * - * The existence of this virtio hdr resource entry means that the firmware - * of this @rproc supports this virtio device. - * - * Currently we support only a single virtio device of type VIRTIO_ID_RPMSG, - * but the plan is to remove this limitation and support any number - * of virtio devices (and of any type). We'll also add support for dynamically - * adding (and removing) virtio devices over the rpmsg bus, but simple - * firmwares that doesn't want to get involved with rpmsg will be able - * to simply use the resource table for this. - * - * Returns 0 on success, or an appropriate error code otherwise - */ -static int rproc_handle_early_vdev(struct rproc *rproc, struct fw_rsc_vdev *rsc, - int avail) +static int +__rproc_handle_vring(struct rproc_vdev *rvdev, struct fw_rsc_vdev *rsc, int i) { - struct rproc_vdev *rvdev; + struct rproc *rproc = rvdev->rproc; + struct device *dev = rproc->dev; + struct fw_rsc_vdev_vring *vring = &rsc->vring[i]; + dma_addr_t dma; + void *va; + int ret, size, notifyid; - /* make sure resource isn't truncated */ - if (sizeof(*rsc) > avail) { - dev_err(rproc->dev, "vdev rsc is truncated\n"); + dev_dbg(dev, "vdev rsc: vring%d: da %x, qsz %d, align %d\n", + i, vring->da, vring->num, vring->align); + + /* make sure reserved bytes are zeroes */ + if (vring->reserved) { + dev_err(dev, "vring rsc has non zero reserved bytes\n"); return -EINVAL; } - /* we only support VIRTIO_ID_RPMSG devices for now */ - if (rsc->id != VIRTIO_ID_RPMSG) { - dev_warn(rproc->dev, "unsupported vdev: %d\n", rsc->id); + /* the firmware must provide the expected queue size */ + if (!vring->num) { + dev_err(dev, "invalid qsz (%d)\n", vring->num); return -EINVAL; } - /* we only support a single vdev per rproc for now */ - if (rproc->rvdev) { - dev_warn(rproc->dev, "redundant vdev entry\n"); + /* actual size of vring (in bytes) */ + size = PAGE_ALIGN(vring_size(vring->num, AMP_VRING_ALIGN)); + + if (!idr_pre_get(&rproc->notifyids, GFP_KERNEL)) { + dev_err(dev, "idr_pre_get failed\n"); + return -ENOMEM; + } + + /* + * Allocate non-cacheable memory for the vring. In the future + * this call will also configure the IOMMU for us + */ + va = dma_alloc_coherent(dev, size, &dma, GFP_KERNEL); + if (!va) { + dev_err(dev, "dma_alloc_coherent failed\n"); return -EINVAL; } - rvdev = kzalloc(sizeof(struct rproc_vdev), GFP_KERNEL); - if (!rvdev) - return -ENOMEM; + /* assign an rproc-wide unique index for this vring */ + /* TODO: assign a notifyid for rvdev updates as well */ + ret = idr_get_new(&rproc->notifyids, &rvdev->vring[i], ¬ifyid); + if (ret) { + dev_err(dev, "idr_get_new failed: %d\n", ret); + dma_free_coherent(dev, size, va, dma); + return ret; + } - /* remember the device features */ - rvdev->dfeatures = rsc->dfeatures; + /* let the rproc know the da and notifyid of this vring */ + /* TODO: expose this to remote processor */ + vring->da = dma; + vring->notifyid = notifyid; - rproc->rvdev = rvdev; - rvdev->rproc = rproc; + dev_dbg(dev, "vring%d: va %p dma %x size %x idr %d\n", i, va, + dma, size, notifyid); + + rvdev->vring[i].len = vring->num; + rvdev->vring[i].va = va; + rvdev->vring[i].dma = dma; + rvdev->vring[i].notifyid = notifyid; + rvdev->vring[i].rvdev = rvdev; return 0; } +static void __rproc_free_vrings(struct rproc_vdev *rvdev, int i) +{ + struct rproc *rproc = rvdev->rproc; + + for (i--; i > 0; i--) { + struct rproc_vring *rvring = &rvdev->vring[i]; + int size = PAGE_ALIGN(vring_size(rvring->len, AMP_VRING_ALIGN)); + + dma_free_coherent(rproc->dev, size, rvring->va, rvring->dma); + idr_remove(&rproc->notifyids, rvring->notifyid); + } +} + /** * rproc_handle_vdev() - handle a vdev fw resource * @rproc: the remote processor * @rsc: the vring resource descriptor * @avail: size of available data (for sanity checking the image) * - * This resource entry requires allocation of non-cacheable memory - * for a virtio vring. Currently we only support two vrings per remote - * processor, required for the virtio rpmsg device. - * - * The 'len' member of @rsc should contain the number of buffers this vring - * support and 'da' should either contain the device address where - * the remote processor is expecting the vring, or indicate that - * dynamically allocation of the vring's device address is supported. - * - * Note: 'da' is currently not handled. This will be revised when the generic - * iommu-based DMA API will arrive, or a dynanic & non-iommu use case show - * up. Meanwhile, statically-addressed iommu-based images should use - * RSC_DEVMEM resource entries to map their require 'da' to the physical - * address of their base CMA region. + * This resource entry requests the host to statically register a virtio + * device (vdev), and setup everything needed to support it. It contains + * everything needed to make it possible: the virtio device id, virtio + * device features, vrings information, virtio config space, etc... + * + * Before registering the vdev, the vrings are allocated from non-cacheable + * physically contiguous memory. Currently we only support two vrings per + * remote processor (temporary limitation). We might also want to consider + * doing the vring allocation only later when ->find_vqs() is invoked, and + * then release them upon ->del_vqs(). + * + * Note: @da is currently not really handled correctly: we dynamically + * allocate it using the DMA API, ignoring requested hard coded addresses, + * and we don't take care of any required IOMMU programming. This is all + * going to be taken care of when the generic iommu-based DMA API will be + * merged. Meanwhile, statically-addressed iommu-based firmware images should + * use RSC_DEVMEM resource entries to map their required @da to the physical + * address of their base CMA region (ouch, hacky!). * * Returns 0 on success, or an appropriate error code otherwise */ @@ -360,8 +392,8 @@ static int rproc_handle_vdev(struct rproc *rproc, struct fw_rsc_vdev *rsc, int avail) { struct device *dev = rproc->dev; - struct rproc_vdev *rvdev = rproc->rvdev; - int i; + struct rproc_vdev *rvdev; + int i, ret; /* make sure resource isn't truncated */ if (sizeof(*rsc) + rsc->num_of_vrings * sizeof(struct fw_rsc_vdev_vring) @@ -379,61 +411,41 @@ static int rproc_handle_vdev(struct rproc *rproc, struct fw_rsc_vdev *rsc, dev_dbg(dev, "vdev rsc: id %d, dfeatures %x, cfg len %d, %d vrings\n", rsc->id, rsc->dfeatures, rsc->config_len, rsc->num_of_vrings); - /* no vdev is in place ? */ - if (!rvdev) { - dev_err(dev, "vring requested without a virtio dev entry\n"); - return -EINVAL; - } - - /* we currently support two vrings per rproc (for rx and tx) */ - if (rsc->num_of_vrings != ARRAY_SIZE(rvdev->vring)) { + /* we currently support only two vrings per rvdev */ + if (rsc->num_of_vrings > ARRAY_SIZE(rvdev->vring)) { dev_err(dev, "too many vrings: %d\n", rsc->num_of_vrings); return -EINVAL; } - /* initialize the vrings */ - for (i = 0; i < rsc->num_of_vrings; i++) { - struct fw_rsc_vdev_vring *vring = &rsc->vring[i]; - dma_addr_t dma; - int size; - void *va; - - /* make sure reserved bytes are zeroes */ - if (vring->reserved) { - dev_err(dev, "vring rsc has non zero reserved bytes\n"); - return -EINVAL; - } + rvdev = kzalloc(sizeof(struct rproc_vdev), GFP_KERNEL); + if (!rvdev) + return -ENOMEM; - /* the firmware must provide the expected queue size */ - if (!vring->num) { - dev_err(dev, "missing expected queue size\n"); - /* potential cleanups are taken care of later on */ - return -EINVAL; - } + rvdev->rproc = rproc; - /* actual size of vring (in bytes) */ - size = PAGE_ALIGN(vring_size(vring->num, AMP_VRING_ALIGN)); + /* allocate the vrings */ + for (i = 0; i < rsc->num_of_vrings; i++) { + ret = __rproc_handle_vring(rvdev, rsc, i); + if (ret) + goto free_vrings; + } - /* - * Allocate non-cacheable memory for the vring. In the future - * this call will also configure the IOMMU for us - */ - va = dma_alloc_coherent(dev, size, &dma, GFP_KERNEL); - if (!va) { - dev_err(dev, "dma_alloc_coherent failed\n"); - /* potential cleanups are taken care of later on */ - return -EINVAL; - } + /* remember the device features */ + rvdev->dfeatures = rsc->dfeatures; - dev_dbg(dev, "vring%d: va %p dma %x qsz %d ring size %x\n", i, - va, dma, vring->num, size); + list_add_tail(&rvdev->node, &rproc->rvdevs); - rvdev->vring[i].len = vring->num; - rvdev->vring[i].va = va; - rvdev->vring[i].dma = dma; - } + /* it is now safe to add the virtio device */ + ret = rproc_add_virtio_dev(rvdev, rsc->id); + if (ret) + goto free_vrings; return 0; + +free_vrings: + __rproc_free_vrings(rvdev, i); + kfree(rvdev); + return ret; } /** @@ -731,7 +743,7 @@ static rproc_handle_resource_t rproc_handle_rsc[] = { [RSC_CARVEOUT] = (rproc_handle_resource_t)rproc_handle_carveout, [RSC_DEVMEM] = (rproc_handle_resource_t)rproc_handle_devmem, [RSC_TRACE] = (rproc_handle_resource_t)rproc_handle_trace, - [RSC_VDEV] = (rproc_handle_resource_t)rproc_handle_vdev, + [RSC_VDEV] = NULL, /* VDEVs were handled upon registrarion */ }; /* handle firmware resource entries before booting the remote processor */ @@ -784,6 +796,7 @@ rproc_handle_virtio_rsc(struct rproc *rproc, struct resource_table *table, int l int offset = table->offset[i]; struct fw_rsc_hdr *hdr = (void *)table + offset; int avail = len - offset - sizeof(*hdr); + struct fw_rsc_vdev *vrsc; /* make sure table isn't truncated */ if (avail < 0) { @@ -793,12 +806,14 @@ rproc_handle_virtio_rsc(struct rproc *rproc, struct resource_table *table, int l dev_dbg(dev, "%s: rsc type %d\n", __func__, hdr->type); - if (hdr->type == RSC_VDEV) { - struct fw_rsc_vdev *vrsc = - (struct fw_rsc_vdev *)hdr->data; - ret = rproc_handle_early_vdev(rproc, vrsc, avail); + if (hdr->type != RSC_VDEV) + continue; + + vrsc = (struct fw_rsc_vdev *)hdr->data; + + ret = rproc_handle_vdev(rproc, vrsc, avail); + if (ret) break; - } } return ret; @@ -889,14 +904,12 @@ static int rproc_handle_resources(struct rproc *rproc, const u8 *elf_data, * @rproc: rproc handle * * This function will free all resources acquired for @rproc, and it - * is called when @rproc shuts down, or just failed booting. + * is called whenever @rproc either shuts down or fails to boot. */ static void rproc_resource_cleanup(struct rproc *rproc) { struct rproc_mem_entry *entry, *tmp; struct device *dev = rproc->dev; - struct rproc_vdev *rvdev = rproc->rvdev; - int i; /* clean up debugfs trace entries */ list_for_each_entry_safe(entry, tmp, &rproc->traces, node) { @@ -906,23 +919,6 @@ static void rproc_resource_cleanup(struct rproc *rproc) kfree(entry); } - /* free the coherent memory allocated for the vrings */ - for (i = 0; rvdev && i < ARRAY_SIZE(rvdev->vring); i++) { - int qsz = rvdev->vring[i].len; - void *va = rvdev->vring[i].va; - int dma = rvdev->vring[i].dma; - - /* virtqueue size is expressed in number of buffers supported */ - if (qsz) { - /* how many bytes does this vring really occupy ? */ - int size = PAGE_ALIGN(vring_size(qsz, AMP_VRING_ALIGN)); - - dma_free_coherent(rproc->dev, size, va, dma); - - rvdev->vring[i].len = 0; - } - } - /* clean up carveout allocations */ list_for_each_entry_safe(entry, tmp, &rproc->carveouts, node) { dma_free_coherent(dev, entry->len, entry->va, entry->dma); @@ -1100,11 +1096,6 @@ static void rproc_fw_config_virtio(const struct firmware *fw, void *context) goto out; } - /* add the virtio device (currently only rpmsg vdevs are supported) */ - ret = rproc_add_rpmsg_vdev(rproc); - if (ret) - goto out; - out: if (fw) release_firmware(fw); @@ -1266,13 +1257,23 @@ EXPORT_SYMBOL(rproc_shutdown); void rproc_release(struct kref *kref) { struct rproc *rproc = container_of(kref, struct rproc, refcount); + struct rproc_vdev *rvdev, *rvtmp; dev_info(rproc->dev, "removing %s\n", rproc->name); rproc_delete_debug_dir(rproc); - /* at this point no one holds a reference to rproc anymore */ - kfree(rproc); + /* clean up remote vdev entries */ + list_for_each_entry_safe(rvdev, rvtmp, &rproc->rvdevs, node) { + __rproc_free_vrings(rvdev, RVDEV_NUM_VRINGS); + list_del(&rvdev->node); + } + + /* + * At this point no one holds a reference to rproc anymore, + * so we can directly unroll rproc_alloc() + */ + rproc_free(rproc); } /* will be called when an rproc is added to the rprocs klist */ @@ -1316,7 +1317,7 @@ static struct rproc *next_rproc(struct klist_iter *i) * use rproc_put() to decrement it back once rproc isn't needed anymore. * * Note: currently this function (and its counterpart rproc_put()) are not - * used anymore by the rpmsg subsystem. We need to scrutinize the use cases + * being used. We need to scrutinize the use cases * that still need them, and see if we can migrate them to use the non * name-based boot/shutdown interface. */ @@ -1391,11 +1392,8 @@ EXPORT_SYMBOL(rproc_put); * firmware. * * If found, those virtio devices will be created and added, so as a result - * of registering this remote processor, additional virtio drivers will be + * of registering this remote processor, additional virtio drivers might be * probed. - * - * Currently, though, we only support a single RPMSG virtio vdev per remote - * processor. */ int rproc_register(struct rproc *rproc) { @@ -1418,7 +1416,7 @@ int rproc_register(struct rproc *rproc) /* * We must retrieve early virtio configuration info from - * the firmware (e.g. whether to register a virtio rpmsg device, + * the firmware (e.g. whether to register a virtio device, * what virtio features does it support, ...). * * We're initiating an asynchronous firmware loading, so we can @@ -1487,9 +1485,12 @@ struct rproc *rproc_alloc(struct device *dev, const char *name, mutex_init(&rproc->lock); + idr_init(&rproc->notifyids); + INIT_LIST_HEAD(&rproc->carveouts); INIT_LIST_HEAD(&rproc->mappings); INIT_LIST_HEAD(&rproc->traces); + INIT_LIST_HEAD(&rproc->rvdevs); rproc->state = RPROC_OFFLINE; @@ -1509,6 +1510,9 @@ EXPORT_SYMBOL(rproc_alloc); */ void rproc_free(struct rproc *rproc) { + idr_remove_all(&rproc->notifyids); + idr_destroy(&rproc->notifyids); + kfree(rproc); } EXPORT_SYMBOL(rproc_free); @@ -1535,18 +1539,22 @@ EXPORT_SYMBOL(rproc_free); */ int rproc_unregister(struct rproc *rproc) { + struct rproc_vdev *rvdev; + if (!rproc) return -EINVAL; /* if rproc is just being registered, wait */ wait_for_completion(&rproc->firmware_loading_complete); - /* was an rpmsg vdev created ? */ - if (rproc->rvdev) - rproc_remove_rpmsg_vdev(rproc); + /* clean up remote vdev entries */ + list_for_each_entry(rvdev, &rproc->rvdevs, node) + rproc_remove_virtio_dev(rvdev); - klist_remove(&rproc->node); + /* the rproc is downref'ed as soon as it's removed from the klist */ + klist_del(&rproc->node); + /* the rproc will only be released after its refcount drops to zero */ kref_put(&rproc->refcount, rproc_release); return 0; diff --git a/drivers/remoteproc/remoteproc_internal.h b/drivers/remoteproc/remoteproc_internal.h index 8b2fc40e92d0..9f336d6bdef3 100644 --- a/drivers/remoteproc/remoteproc_internal.h +++ b/drivers/remoteproc/remoteproc_internal.h @@ -28,9 +28,9 @@ struct rproc; void rproc_release(struct kref *kref); irqreturn_t rproc_vq_interrupt(struct rproc *rproc, int vq_id); -/* from remoteproc_rpmsg.c */ -int rproc_add_rpmsg_vdev(struct rproc *); -void rproc_remove_rpmsg_vdev(struct rproc *rproc); +/* from remoteproc_virtio.c */ +int rproc_add_virtio_dev(struct rproc_vdev *rvdev, int id); +void rproc_remove_virtio_dev(struct rproc_vdev *rvdev); /* from remoteproc_debugfs.c */ void rproc_remove_trace_file(struct dentry *tfile); diff --git a/drivers/remoteproc/remoteproc_virtio.c b/drivers/remoteproc/remoteproc_virtio.c index 78d8527a8fec..07004106c954 100644 --- a/drivers/remoteproc/remoteproc_virtio.c +++ b/drivers/remoteproc/remoteproc_virtio.c @@ -19,7 +19,6 @@ #include #include -#include #include #include #include @@ -30,45 +29,41 @@ #include "remoteproc_internal.h" -/** - * struct rproc_virtio_vq_info - virtqueue state - * @vq_id: a unique index of this virtqueue (unique for this @rproc) - * @rproc: handle to the remote processor - * - * Such a struct will be maintained for every virtqueue we're - * using to communicate with the remote processor - */ -struct rproc_virtio_vq_info { - __u16 vq_id; - struct rproc *rproc; -}; - /* kick the remote processor, and let it know which virtqueue to poke at */ static void rproc_virtio_notify(struct virtqueue *vq) { - struct rproc_virtio_vq_info *rpvq = vq->priv; - struct rproc *rproc = rpvq->rproc; + struct rproc_vring *rvring = vq->priv; + struct rproc *rproc = rvring->rvdev->rproc; + int notifyid = rvring->notifyid; - dev_dbg(rproc->dev, "kicking vq id: %d\n", rpvq->vq_id); + dev_dbg(rproc->dev, "kicking vq index: %d\n", notifyid); - rproc->ops->kick(rproc, rpvq->vq_id); + rproc->ops->kick(rproc, notifyid); } /** * rproc_vq_interrupt() - tell remoteproc that a virtqueue is interrupted * @rproc: handle to the remote processor - * @vq_id: index of the signalled virtqueue + * @notifyid: index of the signalled virtqueue (unique per this @rproc) * * This function should be called by the platform-specific rproc driver, * when the remote processor signals that a specific virtqueue has pending * messages available. * - * Returns IRQ_NONE if no message was found in the @vq_id virtqueue, + * Returns IRQ_NONE if no message was found in the @notifyid virtqueue, * and otherwise returns IRQ_HANDLED. */ -irqreturn_t rproc_vq_interrupt(struct rproc *rproc, int vq_id) +irqreturn_t rproc_vq_interrupt(struct rproc *rproc, int notifyid) { - return vring_interrupt(0, rproc->rvdev->vq[vq_id]); + struct rproc_vring *rvring; + + dev_dbg(rproc->dev, "vq index %d is interrupted\n", notifyid); + + rvring = idr_find(&rproc->notifyids, notifyid); + if (!rvring || !rvring->vq) + return IRQ_NONE; + + return vring_interrupt(0, rvring->vq); } EXPORT_SYMBOL(rproc_vq_interrupt); @@ -77,24 +72,28 @@ static struct virtqueue *rp_find_vq(struct virtio_device *vdev, void (*callback)(struct virtqueue *vq), const char *name) { + struct rproc_vdev *rvdev = vdev_to_rvdev(vdev); struct rproc *rproc = vdev_to_rproc(vdev); - struct rproc_vdev *rvdev = rproc->rvdev; - struct rproc_virtio_vq_info *rpvq; + struct rproc_vring *rvring; struct virtqueue *vq; void *addr; - int ret, len; + int len, size; - rpvq = kmalloc(sizeof(*rpvq), GFP_KERNEL); - if (!rpvq) - return ERR_PTR(-ENOMEM); + /* we're temporarily limited to two virtqueues per rvdev */ + if (id >= ARRAY_SIZE(rvdev->vring)) + return ERR_PTR(-EINVAL); + + rvring = &rvdev->vring[id]; - rpvq->rproc = rproc; - rpvq->vq_id = id; + addr = rvring->va; + len = rvring->len; - addr = rvdev->vring[id].va; - len = rvdev->vring[id].len; + /* zero vring */ + size = vring_size(len, rvring->align); + memset(addr, 0, size); - dev_dbg(rproc->dev, "vring%d: va %p qsz %d\n", id, addr, len); + dev_dbg(rproc->dev, "vring%d: va %p qsz %d notifyid %d\n", + id, addr, len, rvring->notifyid); /* * Create the new vq, and tell virtio we're not interested in @@ -104,32 +103,28 @@ static struct virtqueue *rp_find_vq(struct virtio_device *vdev, rproc_virtio_notify, callback, name); if (!vq) { dev_err(rproc->dev, "vring_new_virtqueue %s failed\n", name); - ret = -ENOMEM; - goto free_rpvq; + return ERR_PTR(-ENOMEM); } - rvdev->vq[id] = vq; - vq->priv = rpvq; + rvring->vq = vq; + vq->priv = rvring; return vq; - -free_rpvq: - kfree(rpvq); - return ERR_PTR(ret); } static void rproc_virtio_del_vqs(struct virtio_device *vdev) { struct virtqueue *vq, *n; struct rproc *rproc = vdev_to_rproc(vdev); + struct rproc_vring *rvring; /* power down the remote processor before deleting vqs */ rproc_shutdown(rproc); list_for_each_entry_safe(vq, n, &vdev->vqs, list) { - struct rproc_virtio_vq_info *rpvq = vq->priv; + rvring = vq->priv; + rvring->vq = NULL; vring_del_virtqueue(vq); - kfree(rpvq); } } @@ -141,10 +136,6 @@ static int rproc_virtio_find_vqs(struct virtio_device *vdev, unsigned nvqs, struct rproc *rproc = vdev_to_rproc(vdev); int i, ret; - /* we maintain two virtqueues per remote processor (for RX and TX) */ - if (nvqs != 2) - return -EINVAL; - for (i = 0; i < nvqs; ++i) { vqs[i] = rp_find_vq(vdev, i, callbacks[i], names[i]); if (IS_ERR(vqs[i])) { @@ -170,7 +161,7 @@ error: /* * We don't support yet real virtio status semantics. * - * The plan is to provide this via the VIRTIO HDR resource entry + * The plan is to provide this via the VDEV resource entry * which is part of the firmware: this way the remote processor * will be able to access the status values as set by us. */ @@ -181,7 +172,7 @@ static u8 rproc_virtio_get_status(struct virtio_device *vdev) static void rproc_virtio_set_status(struct virtio_device *vdev, u8 status) { - dev_dbg(&vdev->dev, "new status: %d\n", status); + dev_dbg(&vdev->dev, "status: %d\n", status); } static void rproc_virtio_reset(struct virtio_device *vdev) @@ -192,15 +183,14 @@ static void rproc_virtio_reset(struct virtio_device *vdev) /* provide the vdev features as retrieved from the firmware */ static u32 rproc_virtio_get_features(struct virtio_device *vdev) { - struct rproc *rproc = vdev_to_rproc(vdev); + struct rproc_vdev *rvdev = vdev_to_rvdev(vdev); - /* we only support a single vdev device for now */ - return rproc->rvdev->dfeatures; + return rvdev->dfeatures; } static void rproc_virtio_finalize_features(struct virtio_device *vdev) { - struct rproc *rproc = vdev_to_rproc(vdev); + struct rproc_vdev *rvdev = vdev_to_rvdev(vdev); /* Give virtio_ring a chance to accept features */ vring_transport_features(vdev); @@ -214,7 +204,7 @@ static void rproc_virtio_finalize_features(struct virtio_device *vdev) * fixed as part of a small resource table overhaul and then an * extension of the virtio resource entries. */ - rproc->rvdev->gfeatures = vdev->features[0]; + rvdev->gfeatures = vdev->features[0]; } static struct virtio_config_ops rproc_virtio_config_ops = { @@ -244,26 +234,25 @@ static void rproc_vdev_release(struct device *dev) } /** - * rproc_add_rpmsg_vdev() - create an rpmsg virtio device - * @rproc: the rproc handle + * rproc_add_virtio_dev() - register an rproc-induced virtio device + * @rvdev: the remote vdev * - * This function is called if virtio rpmsg support was found in the - * firmware of the remote processor. + * This function registers a virtio device. This vdev's partent is + * the rproc device. * - * Today we only support creating a single rpmsg vdev (virtio device), - * but the plan is to remove this limitation. At that point this interface - * will be revised/extended. + * Returns 0 on success or an appropriate error value otherwise. */ -int rproc_add_rpmsg_vdev(struct rproc *rproc) +int rproc_add_virtio_dev(struct rproc_vdev *rvdev, int id) { + struct rproc *rproc = rvdev->rproc; struct device *dev = rproc->dev; - struct rproc_vdev *rvdev = rproc->rvdev; + struct virtio_device *vdev = &rvdev->vdev; int ret; - rvdev->vdev.id.device = VIRTIO_ID_RPMSG, - rvdev->vdev.config = &rproc_virtio_config_ops, - rvdev->vdev.dev.parent = dev; - rvdev->vdev.dev.release = rproc_vdev_release; + vdev->id.device = id, + vdev->config = &rproc_virtio_config_ops, + vdev->dev.parent = dev; + vdev->dev.release = rproc_vdev_release; /* * We're indirectly making a non-temporary copy of the rproc pointer @@ -275,25 +264,26 @@ int rproc_add_rpmsg_vdev(struct rproc *rproc) */ kref_get(&rproc->refcount); - ret = register_virtio_device(&rvdev->vdev); + ret = register_virtio_device(vdev); if (ret) { kref_put(&rproc->refcount, rproc_release); dev_err(dev, "failed to register vdev: %d\n", ret); + goto out; } + dev_info(dev, "registered %s (type %d)\n", dev_name(&vdev->dev), id); + +out: return ret; } /** - * rproc_remove_rpmsg_vdev() - remove an rpmsg vdev device - * @rproc: the rproc handle + * rproc_remove_virtio_dev() - remove an rproc-induced virtio device + * @rvdev: the remote vdev * - * This function is called whenever @rproc is removed _iff_ an rpmsg - * vdev was created beforehand. + * This function unregisters an existing virtio device. */ -void rproc_remove_rpmsg_vdev(struct rproc *rproc) +void rproc_remove_virtio_dev(struct rproc_vdev *rvdev) { - struct rproc_vdev *rvdev = rproc->rvdev; - unregister_virtio_device(&rvdev->vdev); } diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h index 6040f831f626..7750d8a30933 100644 --- a/include/linux/remoteproc.h +++ b/include/linux/remoteproc.h @@ -41,6 +41,7 @@ #include #include #include +#include /* * The alignment between the consumer and producer parts of the vring. @@ -387,7 +388,8 @@ enum rproc_state { * @mappings: list of iommu mappings we initiated, needed on shutdown * @firmware_loading_complete: marks e/o asynchronous firmware loading * @bootaddr: address of first instruction to boot rproc with (optional) - * @rvdev: virtio device (we only support a single rpmsg virtio device for now) + * @rvdevs: list of remote virtio devices + * @notifyids: idr for dynamically assigning rproc-wide unique notify ids */ struct rproc { struct klist_node node; @@ -408,23 +410,47 @@ struct rproc { struct list_head mappings; struct completion firmware_loading_complete; u32 bootaddr; + struct list_head rvdevs; + struct idr notifyids; +}; + +/* we currently support only two vrings per rvdev */ +#define RVDEV_NUM_VRINGS 2 + +/** + * struct rproc_vring - remoteproc vring state + * @va: virtual address + * @dma: dma address + * @len: length, in bytes + * @da: device address + * @notifyid: rproc-specific unique vring index + * @rvdev: remote vdev + * @vq: the virtqueue of this vring + */ +struct rproc_vring { + void *va; + dma_addr_t dma; + int len; + u32 da; + int notifyid; struct rproc_vdev *rvdev; + struct virtqueue *vq; }; /** * struct rproc_vdev - remoteproc state for a supported virtio device + * @node: list node * @rproc: the rproc handle * @vdev: the virio device - * @vq: the virtqueues for this vdev * @vring: the vrings for this vdev * @dfeatures: virtio device features * @gfeatures: virtio guest features */ struct rproc_vdev { + struct list_head node; struct rproc *rproc; struct virtio_device vdev; - struct virtqueue *vq[2]; - struct rproc_mem_entry vring[2]; + struct rproc_vring vring[RVDEV_NUM_VRINGS]; unsigned long dfeatures; unsigned long gfeatures; }; @@ -442,9 +468,14 @@ int rproc_unregister(struct rproc *rproc); int rproc_boot(struct rproc *rproc); void rproc_shutdown(struct rproc *rproc); +static inline struct rproc_vdev *vdev_to_rvdev(struct virtio_device *vdev) +{ + return container_of(vdev, struct rproc_vdev, vdev); +} + static inline struct rproc *vdev_to_rproc(struct virtio_device *vdev) { - struct rproc_vdev *rvdev = container_of(vdev, struct rproc_vdev, vdev); + struct rproc_vdev *rvdev = vdev_to_rvdev(vdev); return rvdev->rproc; } -- cgit v1.2.3 From 63140e0ed2e69bdafe62bc19fd6551d9213f80a7 Mon Sep 17 00:00:00 2001 From: Ohad Ben-Cohen Date: Wed, 29 Feb 2012 14:42:13 +0200 Subject: remoteproc: remove the hardcoded vring alignment Remove the hardcoded vring alignment of 4096 bytes, and instead utilize tha vring alignment as specified in the resource table. This is needed for remote processors that have rigid memory requirement, and which have found the alignment of 4096 bytes to be excessively big. Signed-off-by: Ohad Ben-Cohen Cc: Brian Swetland Cc: Iliyan Malchev Cc: Arnd Bergmann Cc: Grant Likely Cc: Rusty Russell Cc: Mark Grosen Cc: John Williams Cc: Michal Simek Cc: Loic PALLARDY Cc: Ludovic BARRE Cc: Omar Ramirez Luna Cc: Guzman Lugo Fernando Cc: Anna Suman Cc: Clark Rob Cc: Stephen Boyd Cc: Saravana Kannan Cc: David Brown Cc: Kieran Bingham Cc: Tony Lindgren --- drivers/remoteproc/remoteproc_core.c | 12 +++++++----- drivers/remoteproc/remoteproc_virtio.c | 2 +- include/linux/remoteproc.h | 9 ++------- 3 files changed, 10 insertions(+), 13 deletions(-) (limited to 'include/linux') diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c index ca02f128b435..9be5dadaa3a3 100644 --- a/drivers/remoteproc/remoteproc_core.c +++ b/drivers/remoteproc/remoteproc_core.c @@ -298,14 +298,15 @@ __rproc_handle_vring(struct rproc_vdev *rvdev, struct fw_rsc_vdev *rsc, int i) return -EINVAL; } - /* the firmware must provide the expected queue size */ - if (!vring->num) { - dev_err(dev, "invalid qsz (%d)\n", vring->num); + /* verify queue size and vring alignment are sane */ + if (!vring->num || !vring->align) { + dev_err(dev, "invalid qsz (%d) or alignment (%d)\n", + vring->num, vring->align); return -EINVAL; } /* actual size of vring (in bytes) */ - size = PAGE_ALIGN(vring_size(vring->num, AMP_VRING_ALIGN)); + size = PAGE_ALIGN(vring_size(vring->num, vring->align)); if (!idr_pre_get(&rproc->notifyids, GFP_KERNEL)) { dev_err(dev, "idr_pre_get failed\n"); @@ -340,6 +341,7 @@ __rproc_handle_vring(struct rproc_vdev *rvdev, struct fw_rsc_vdev *rsc, int i) dma, size, notifyid); rvdev->vring[i].len = vring->num; + rvdev->vring[i].align = vring->align; rvdev->vring[i].va = va; rvdev->vring[i].dma = dma; rvdev->vring[i].notifyid = notifyid; @@ -354,7 +356,7 @@ static void __rproc_free_vrings(struct rproc_vdev *rvdev, int i) for (i--; i > 0; i--) { struct rproc_vring *rvring = &rvdev->vring[i]; - int size = PAGE_ALIGN(vring_size(rvring->len, AMP_VRING_ALIGN)); + int size = PAGE_ALIGN(vring_size(rvring->len, rvring->align)); dma_free_coherent(rproc->dev, size, rvring->va, rvring->dma); idr_remove(&rproc->notifyids, rvring->notifyid); diff --git a/drivers/remoteproc/remoteproc_virtio.c b/drivers/remoteproc/remoteproc_virtio.c index 07004106c954..ecf612130750 100644 --- a/drivers/remoteproc/remoteproc_virtio.c +++ b/drivers/remoteproc/remoteproc_virtio.c @@ -99,7 +99,7 @@ static struct virtqueue *rp_find_vq(struct virtio_device *vdev, * Create the new vq, and tell virtio we're not interested in * the 'weak' smp barriers, since we're talking with a real device. */ - vq = vring_new_virtqueue(len, AMP_VRING_ALIGN, vdev, false, addr, + vq = vring_new_virtqueue(len, rvring->align, vdev, false, addr, rproc_virtio_notify, callback, name); if (!vq) { dev_err(rproc->dev, "vring_new_virtqueue %s failed\n", name); diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h index 7750d8a30933..f1ffabb978d3 100644 --- a/include/linux/remoteproc.h +++ b/include/linux/remoteproc.h @@ -43,13 +43,6 @@ #include #include -/* - * The alignment between the consumer and producer parts of the vring. - * Note: this is part of the "wire" protocol. If you change this, you need - * to update your peers too. - */ -#define AMP_VRING_ALIGN (4096) - /** * struct resource_table - firmware resource table header * @ver: version number @@ -423,6 +416,7 @@ struct rproc { * @dma: dma address * @len: length, in bytes * @da: device address + * @align: vring alignment * @notifyid: rproc-specific unique vring index * @rvdev: remote vdev * @vq: the virtqueue of this vring @@ -432,6 +426,7 @@ struct rproc_vring { dma_addr_t dma; int len; u32 da; + u32 align; int notifyid; struct rproc_vdev *rvdev; struct virtqueue *vq; -- cgit v1.2.3 From 9a01be1715b6a027765701ca09ba0401ff066fc5 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 6 Mar 2012 21:15:51 +0100 Subject: pinctrl: split pincontrol states into its own header Move the pin control state defines into its own header file, since it is used both by machine.h which is facing the platform and by consumer.h which is facing the drivers, and pinctrl.h which is pinctrl-driver internal, let's not have each and every .h file include all others, then isolation is moot. Acked-by: Stephen Warren Signed-off-by: Linus Walleij --- include/linux/pinctrl/consumer.h | 2 +- include/linux/pinctrl/machine.h | 2 +- include/linux/pinctrl/pinctrl-state.h | 6 ++++++ include/linux/pinctrl/pinctrl.h | 3 +-- 4 files changed, 9 insertions(+), 4 deletions(-) create mode 100644 include/linux/pinctrl/pinctrl-state.h (limited to 'include/linux') diff --git a/include/linux/pinctrl/consumer.h b/include/linux/pinctrl/consumer.h index 9ad5896cfa0e..191e72688481 100644 --- a/include/linux/pinctrl/consumer.h +++ b/include/linux/pinctrl/consumer.h @@ -15,7 +15,7 @@ #include #include #include -#include "pinctrl.h" +#include "pinctrl-state.h" /* This struct is private to the core and should be regarded as a cookie */ struct pinctrl; diff --git a/include/linux/pinctrl/machine.h b/include/linux/pinctrl/machine.h index 3fd2f9dfc645..fee4349364f7 100644 --- a/include/linux/pinctrl/machine.h +++ b/include/linux/pinctrl/machine.h @@ -12,7 +12,7 @@ #ifndef __LINUX_PINCTRL_MACHINE_H #define __LINUX_PINCTRL_MACHINE_H -#include "pinctrl.h" +#include "pinctrl-state.h" enum pinctrl_map_type { PIN_MAP_TYPE_INVALID, diff --git a/include/linux/pinctrl/pinctrl-state.h b/include/linux/pinctrl/pinctrl-state.h new file mode 100644 index 000000000000..3920e28b4da7 --- /dev/null +++ b/include/linux/pinctrl/pinctrl-state.h @@ -0,0 +1,6 @@ +/* + * Standard pin control state definitions + */ + +#define PINCTRL_STATE_DEFAULT "default" +#define PINCTRL_STATE_IDLE "idle" diff --git a/include/linux/pinctrl/pinctrl.h b/include/linux/pinctrl/pinctrl.h index bbdd7e16bada..e9e94eb52d79 100644 --- a/include/linux/pinctrl/pinctrl.h +++ b/include/linux/pinctrl/pinctrl.h @@ -17,8 +17,7 @@ #include #include #include - -#define PINCTRL_STATE_DEFAULT "default" +#include "pinctrl-state.h" struct pinctrl_dev; struct pinmux_ops; -- cgit v1.2.3 From 0acfb076f7987bd4bb5cd5de879ba3e3e71724e9 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Tue, 6 Mar 2012 12:12:12 -0700 Subject: pinctrl: forward-declare struct device Add a dummy declaration of struct device to avoid the following warning: In file included from include/linux/pinctrl/machine.h:15:0, from arch/arm/mach-tegra/board-pinmux.h:18, from arch/arm/mach-tegra/board-trimslice-pinmux.c:20: include/linux/pinctrl/pinctrl.h:115:12: warning: 'struct device' declared inside parameter list [enabled by default] include/linux/pinctrl/pinctrl.h:115:12: warning: its scope is only this definition or declaration, which is probably not what you want [enabled by default] Signed-off-by: Stephen Warren Signed-off-by: Linus Walleij --- include/linux/pinctrl/pinctrl.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include/linux') diff --git a/include/linux/pinctrl/pinctrl.h b/include/linux/pinctrl/pinctrl.h index e9e94eb52d79..4e9f0788c221 100644 --- a/include/linux/pinctrl/pinctrl.h +++ b/include/linux/pinctrl/pinctrl.h @@ -19,6 +19,7 @@ #include #include "pinctrl-state.h" +struct device; struct pinctrl_dev; struct pinmux_ops; struct pinconf_ops; -- cgit v1.2.3 From 8a16a701ad787b6db2949766341c5ad1b551de9f Mon Sep 17 00:00:00 2001 From: Heiko Stübner Date: Fri, 2 Mar 2012 13:57:44 +0100 Subject: regulator: Remove bq24022 regulator driver The bq24022 driver is just a specialised form of a gpio-regulator. As all former users of it now use the gpio-regulator directly, there is no need to keep it around. Signed-off-by: Heiko Stuebner Acked-by: Mark Brown Signed-off-by: Haojian Zhuang --- drivers/regulator/Kconfig | 8 -- drivers/regulator/Makefile | 1 - drivers/regulator/bq24022.c | 162 -------------------------------------- include/linux/regulator/bq24022.h | 24 ------ 4 files changed, 195 deletions(-) delete mode 100644 drivers/regulator/bq24022.c delete mode 100644 include/linux/regulator/bq24022.h (limited to 'include/linux') diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 7a61b17ddd04..740f468ba65f 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -74,14 +74,6 @@ config REGULATOR_GPIO and the platform has to provide a mapping of GPIO-states to target volts/amps. -config REGULATOR_BQ24022 - tristate "TI bq24022 Dual Input 1-Cell Li-Ion Charger IC" - help - This driver controls a TI bq24022 Charger attached via - GPIOs. The provided current regulator can enable/disable - charging select between 100 mA and 500 mA charging current - limit. - config REGULATOR_MAX1586 tristate "Maxim 1586/1587 voltage regulator" depends on I2C diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 503bac87715e..f53cf8082c62 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -11,7 +11,6 @@ obj-$(CONFIG_REGULATOR_USERSPACE_CONSUMER) += userspace-consumer.o obj-$(CONFIG_REGULATOR_GPIO) += gpio-regulator.o obj-$(CONFIG_REGULATOR_AD5398) += ad5398.o -obj-$(CONFIG_REGULATOR_BQ24022) += bq24022.o obj-$(CONFIG_REGULATOR_LP3971) += lp3971.o obj-$(CONFIG_REGULATOR_LP3972) += lp3972.o obj-$(CONFIG_REGULATOR_MAX1586) += max1586.o diff --git a/drivers/regulator/bq24022.c b/drivers/regulator/bq24022.c deleted file mode 100644 index 9fab6d1bbe80..000000000000 --- a/drivers/regulator/bq24022.c +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Support for TI bq24022 (bqTINY-II) Dual Input (USB/AC Adpater) - * 1-Cell Li-Ion Charger connected via GPIOs. - * - * Copyright (c) 2008 Philipp Zabel - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include - - -static int bq24022_set_current_limit(struct regulator_dev *rdev, - int min_uA, int max_uA) -{ - struct bq24022_mach_info *pdata = rdev_get_drvdata(rdev); - - dev_dbg(rdev_get_dev(rdev), "setting current limit to %s mA\n", - max_uA >= 500000 ? "500" : "100"); - - /* REVISIT: maybe return error if min_uA != 0 ? */ - gpio_set_value(pdata->gpio_iset2, max_uA >= 500000); - return 0; -} - -static int bq24022_get_current_limit(struct regulator_dev *rdev) -{ - struct bq24022_mach_info *pdata = rdev_get_drvdata(rdev); - - return gpio_get_value(pdata->gpio_iset2) ? 500000 : 100000; -} - -static int bq24022_enable(struct regulator_dev *rdev) -{ - struct bq24022_mach_info *pdata = rdev_get_drvdata(rdev); - - dev_dbg(rdev_get_dev(rdev), "enabling charger\n"); - - gpio_set_value(pdata->gpio_nce, 0); - return 0; -} - -static int bq24022_disable(struct regulator_dev *rdev) -{ - struct bq24022_mach_info *pdata = rdev_get_drvdata(rdev); - - dev_dbg(rdev_get_dev(rdev), "disabling charger\n"); - - gpio_set_value(pdata->gpio_nce, 1); - return 0; -} - -static int bq24022_is_enabled(struct regulator_dev *rdev) -{ - struct bq24022_mach_info *pdata = rdev_get_drvdata(rdev); - - return !gpio_get_value(pdata->gpio_nce); -} - -static struct regulator_ops bq24022_ops = { - .set_current_limit = bq24022_set_current_limit, - .get_current_limit = bq24022_get_current_limit, - .enable = bq24022_enable, - .disable = bq24022_disable, - .is_enabled = bq24022_is_enabled, -}; - -static struct regulator_desc bq24022_desc = { - .name = "bq24022", - .ops = &bq24022_ops, - .type = REGULATOR_CURRENT, - .owner = THIS_MODULE, -}; - -static int __init bq24022_probe(struct platform_device *pdev) -{ - struct bq24022_mach_info *pdata = pdev->dev.platform_data; - struct regulator_dev *bq24022; - int ret; - - if (!pdata || !pdata->gpio_nce || !pdata->gpio_iset2) - return -EINVAL; - - ret = gpio_request(pdata->gpio_nce, "ncharge_en"); - if (ret) { - dev_dbg(&pdev->dev, "couldn't request nCE GPIO: %d\n", - pdata->gpio_nce); - goto err_ce; - } - ret = gpio_request(pdata->gpio_iset2, "charge_mode"); - if (ret) { - dev_dbg(&pdev->dev, "couldn't request ISET2 GPIO: %d\n", - pdata->gpio_iset2); - goto err_iset2; - } - ret = gpio_direction_output(pdata->gpio_iset2, 0); - ret = gpio_direction_output(pdata->gpio_nce, 1); - - bq24022 = regulator_register(&bq24022_desc, &pdev->dev, - pdata->init_data, pdata, NULL); - if (IS_ERR(bq24022)) { - dev_dbg(&pdev->dev, "couldn't register regulator\n"); - ret = PTR_ERR(bq24022); - goto err_reg; - } - platform_set_drvdata(pdev, bq24022); - dev_dbg(&pdev->dev, "registered regulator\n"); - - return 0; -err_reg: - gpio_free(pdata->gpio_iset2); -err_iset2: - gpio_free(pdata->gpio_nce); -err_ce: - return ret; -} - -static int __devexit bq24022_remove(struct platform_device *pdev) -{ - struct bq24022_mach_info *pdata = pdev->dev.platform_data; - struct regulator_dev *bq24022 = platform_get_drvdata(pdev); - - regulator_unregister(bq24022); - gpio_free(pdata->gpio_iset2); - gpio_free(pdata->gpio_nce); - - return 0; -} - -static struct platform_driver bq24022_driver = { - .driver = { - .name = "bq24022", - }, - .remove = __devexit_p(bq24022_remove), -}; - -static int __init bq24022_init(void) -{ - return platform_driver_probe(&bq24022_driver, bq24022_probe); -} - -static void __exit bq24022_exit(void) -{ - platform_driver_unregister(&bq24022_driver); -} - -module_init(bq24022_init); -module_exit(bq24022_exit); - -MODULE_AUTHOR("Philipp Zabel"); -MODULE_DESCRIPTION("TI bq24022 Li-Ion Charger driver"); -MODULE_LICENSE("GPL"); diff --git a/include/linux/regulator/bq24022.h b/include/linux/regulator/bq24022.h deleted file mode 100644 index a6d014005d49..000000000000 --- a/include/linux/regulator/bq24022.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Support for TI bq24022 (bqTINY-II) Dual Input (USB/AC Adpater) - * 1-Cell Li-Ion Charger connected via GPIOs. - * - * Copyright (c) 2008 Philipp Zabel - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - */ - -struct regulator_init_data; - -/** - * bq24022_mach_info - platform data for bq24022 - * @gpio_nce: GPIO line connected to the nCE pin, used to enable / disable charging - * @gpio_iset2: GPIO line connected to the ISET2 pin, used to limit charging current to 100 mA / 500 mA - */ -struct bq24022_mach_info { - int gpio_nce; - int gpio_iset2; - struct regulator_init_data *init_data; -}; -- cgit v1.2.3 From 6365bead25efc84a4cf4aa9b0a7638f8a970cdff Mon Sep 17 00:00:00 2001 From: Russell King Date: Mon, 9 Jan 2012 21:44:07 +0000 Subject: DMA: sa11x0: add SA-11x0 DMA driver Add support for the SA-11x0 DMA driver, which replaces the private API version in arch/arm/mach-sa1100/dma.c. We model this as a set of virtual DMA channels, one for each request signal, and assign the virtual DMA channel to a physical DMA channel when there is work to be done. This allows DMA users to claim their channels, and hold them while not in use, without affecting the availability of the physical channels. Another advantage over this approach, compared to the private version, is that a channel can be reconfigured on the fly without having to release and re-request it - which for the IrDA driver, allows us to use DMA for SIR mode transmit without eating up three physical channels. As IrDA is half-duplex, we actually only need one physical channel, and this architecture allows us to achieve that. Signed-off-by: Russell King --- drivers/dma/Kconfig | 9 + drivers/dma/Makefile | 1 + drivers/dma/sa11x0-dma.c | 1109 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/sa11x0-dma.h | 24 + 4 files changed, 1143 insertions(+) create mode 100644 drivers/dma/sa11x0-dma.c create mode 100644 include/linux/sa11x0-dma.h (limited to 'include/linux') diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index f1a274994bb1..4a6c46dea8a0 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -252,6 +252,15 @@ config EP93XX_DMA help Enable support for the Cirrus Logic EP93xx M2P/M2M DMA controller. +config DMA_SA11X0 + tristate "SA-11x0 DMA support" + depends on ARCH_SA1100 + select DMA_ENGINE + help + Support the DMA engine found on Intel StrongARM SA-1100 and + SA-1110 SoCs. This DMA engine can only be used with on-chip + devices. + config DMA_ENGINE bool diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index 009a222e8283..86b795baba98 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -27,3 +27,4 @@ obj-$(CONFIG_PL330_DMA) += pl330.o obj-$(CONFIG_PCH_DMA) += pch_dma.o obj-$(CONFIG_AMBA_PL08X) += amba-pl08x.o obj-$(CONFIG_EP93XX_DMA) += ep93xx_dma.o +obj-$(CONFIG_DMA_SA11X0) += sa11x0-dma.o diff --git a/drivers/dma/sa11x0-dma.c b/drivers/dma/sa11x0-dma.c new file mode 100644 index 000000000000..16a6b48883cf --- /dev/null +++ b/drivers/dma/sa11x0-dma.c @@ -0,0 +1,1109 @@ +/* + * SA11x0 DMAengine support + * + * Copyright (C) 2012 Russell King + * Derived in part from arch/arm/mach-sa1100/dma.c, + * Copyright (C) 2000, 2001 by Nicolas Pitre + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define NR_PHY_CHAN 6 +#define DMA_ALIGN 3 +#define DMA_MAX_SIZE 0x1fff +#define DMA_CHUNK_SIZE 0x1000 + +#define DMA_DDAR 0x00 +#define DMA_DCSR_S 0x04 +#define DMA_DCSR_C 0x08 +#define DMA_DCSR_R 0x0c +#define DMA_DBSA 0x10 +#define DMA_DBTA 0x14 +#define DMA_DBSB 0x18 +#define DMA_DBTB 0x1c +#define DMA_SIZE 0x20 + +#define DCSR_RUN (1 << 0) +#define DCSR_IE (1 << 1) +#define DCSR_ERROR (1 << 2) +#define DCSR_DONEA (1 << 3) +#define DCSR_STRTA (1 << 4) +#define DCSR_DONEB (1 << 5) +#define DCSR_STRTB (1 << 6) +#define DCSR_BIU (1 << 7) + +#define DDAR_RW (1 << 0) /* 0 = W, 1 = R */ +#define DDAR_E (1 << 1) /* 0 = LE, 1 = BE */ +#define DDAR_BS (1 << 2) /* 0 = BS4, 1 = BS8 */ +#define DDAR_DW (1 << 3) /* 0 = 8b, 1 = 16b */ +#define DDAR_Ser0UDCTr (0x0 << 4) +#define DDAR_Ser0UDCRc (0x1 << 4) +#define DDAR_Ser1SDLCTr (0x2 << 4) +#define DDAR_Ser1SDLCRc (0x3 << 4) +#define DDAR_Ser1UARTTr (0x4 << 4) +#define DDAR_Ser1UARTRc (0x5 << 4) +#define DDAR_Ser2ICPTr (0x6 << 4) +#define DDAR_Ser2ICPRc (0x7 << 4) +#define DDAR_Ser3UARTTr (0x8 << 4) +#define DDAR_Ser3UARTRc (0x9 << 4) +#define DDAR_Ser4MCP0Tr (0xa << 4) +#define DDAR_Ser4MCP0Rc (0xb << 4) +#define DDAR_Ser4MCP1Tr (0xc << 4) +#define DDAR_Ser4MCP1Rc (0xd << 4) +#define DDAR_Ser4SSPTr (0xe << 4) +#define DDAR_Ser4SSPRc (0xf << 4) + +struct sa11x0_dma_sg { + u32 addr; + u32 len; +}; + +struct sa11x0_dma_desc { + struct dma_async_tx_descriptor tx; + u32 ddar; + size_t size; + + /* maybe protected by c->lock */ + struct list_head node; + unsigned sglen; + struct sa11x0_dma_sg sg[0]; +}; + +struct sa11x0_dma_phy; + +struct sa11x0_dma_chan { + struct dma_chan chan; + spinlock_t lock; + dma_cookie_t lc; + + /* protected by c->lock */ + struct sa11x0_dma_phy *phy; + enum dma_status status; + struct list_head desc_submitted; + struct list_head desc_issued; + + /* protected by d->lock */ + struct list_head node; + + u32 ddar; + const char *name; +}; + +struct sa11x0_dma_phy { + void __iomem *base; + struct sa11x0_dma_dev *dev; + unsigned num; + + struct sa11x0_dma_chan *vchan; + + /* Protected by c->lock */ + unsigned sg_load; + struct sa11x0_dma_desc *txd_load; + unsigned sg_done; + struct sa11x0_dma_desc *txd_done; +#ifdef CONFIG_PM_SLEEP + u32 dbs[2]; + u32 dbt[2]; + u32 dcsr; +#endif +}; + +struct sa11x0_dma_dev { + struct dma_device slave; + void __iomem *base; + spinlock_t lock; + struct tasklet_struct task; + struct list_head chan_pending; + struct list_head desc_complete; + struct sa11x0_dma_phy phy[NR_PHY_CHAN]; +}; + +static struct sa11x0_dma_chan *to_sa11x0_dma_chan(struct dma_chan *chan) +{ + return container_of(chan, struct sa11x0_dma_chan, chan); +} + +static struct sa11x0_dma_dev *to_sa11x0_dma(struct dma_device *dmadev) +{ + return container_of(dmadev, struct sa11x0_dma_dev, slave); +} + +static struct sa11x0_dma_desc *to_sa11x0_dma_tx(struct dma_async_tx_descriptor *tx) +{ + return container_of(tx, struct sa11x0_dma_desc, tx); +} + +static struct sa11x0_dma_desc *sa11x0_dma_next_desc(struct sa11x0_dma_chan *c) +{ + if (list_empty(&c->desc_issued)) + return NULL; + + return list_first_entry(&c->desc_issued, struct sa11x0_dma_desc, node); +} + +static void sa11x0_dma_start_desc(struct sa11x0_dma_phy *p, struct sa11x0_dma_desc *txd) +{ + list_del(&txd->node); + p->txd_load = txd; + p->sg_load = 0; + + dev_vdbg(p->dev->slave.dev, "pchan %u: txd %p[%x]: starting: DDAR:%x\n", + p->num, txd, txd->tx.cookie, txd->ddar); +} + +static void noinline sa11x0_dma_start_sg(struct sa11x0_dma_phy *p, + struct sa11x0_dma_chan *c) +{ + struct sa11x0_dma_desc *txd = p->txd_load; + struct sa11x0_dma_sg *sg; + void __iomem *base = p->base; + unsigned dbsx, dbtx; + u32 dcsr; + + if (!txd) + return; + + dcsr = readl_relaxed(base + DMA_DCSR_R); + + /* Don't try to load the next transfer if both buffers are started */ + if ((dcsr & (DCSR_STRTA | DCSR_STRTB)) == (DCSR_STRTA | DCSR_STRTB)) + return; + + if (p->sg_load == txd->sglen) { + struct sa11x0_dma_desc *txn = sa11x0_dma_next_desc(c); + + /* + * We have reached the end of the current descriptor. + * Peek at the next descriptor, and if compatible with + * the current, start processing it. + */ + if (txn && txn->ddar == txd->ddar) { + txd = txn; + sa11x0_dma_start_desc(p, txn); + } else { + p->txd_load = NULL; + return; + } + } + + sg = &txd->sg[p->sg_load++]; + + /* Select buffer to load according to channel status */ + if (((dcsr & (DCSR_BIU | DCSR_STRTB)) == (DCSR_BIU | DCSR_STRTB)) || + ((dcsr & (DCSR_BIU | DCSR_STRTA)) == 0)) { + dbsx = DMA_DBSA; + dbtx = DMA_DBTA; + dcsr = DCSR_STRTA | DCSR_IE | DCSR_RUN; + } else { + dbsx = DMA_DBSB; + dbtx = DMA_DBTB; + dcsr = DCSR_STRTB | DCSR_IE | DCSR_RUN; + } + + writel_relaxed(sg->addr, base + dbsx); + writel_relaxed(sg->len, base + dbtx); + writel(dcsr, base + DMA_DCSR_S); + + dev_dbg(p->dev->slave.dev, "pchan %u: load: DCSR:%02x DBS%c:%08x DBT%c:%08x\n", + p->num, dcsr, + 'A' + (dbsx == DMA_DBSB), sg->addr, + 'A' + (dbtx == DMA_DBTB), sg->len); +} + +static void noinline sa11x0_dma_complete(struct sa11x0_dma_phy *p, + struct sa11x0_dma_chan *c) +{ + struct sa11x0_dma_desc *txd = p->txd_done; + + if (++p->sg_done == txd->sglen) { + struct sa11x0_dma_dev *d = p->dev; + + dev_vdbg(d->slave.dev, "pchan %u: txd %p[%x]: completed\n", + p->num, p->txd_done, p->txd_done->tx.cookie); + + c->lc = txd->tx.cookie; + + spin_lock(&d->lock); + list_add_tail(&txd->node, &d->desc_complete); + spin_unlock(&d->lock); + + p->sg_done = 0; + p->txd_done = p->txd_load; + + tasklet_schedule(&d->task); + } + + sa11x0_dma_start_sg(p, c); +} + +static irqreturn_t sa11x0_dma_irq(int irq, void *dev_id) +{ + struct sa11x0_dma_phy *p = dev_id; + struct sa11x0_dma_dev *d = p->dev; + struct sa11x0_dma_chan *c; + u32 dcsr; + + dcsr = readl_relaxed(p->base + DMA_DCSR_R); + if (!(dcsr & (DCSR_ERROR | DCSR_DONEA | DCSR_DONEB))) + return IRQ_NONE; + + /* Clear reported status bits */ + writel_relaxed(dcsr & (DCSR_ERROR | DCSR_DONEA | DCSR_DONEB), + p->base + DMA_DCSR_C); + + dev_dbg(d->slave.dev, "pchan %u: irq: DCSR:%02x\n", p->num, dcsr); + + if (dcsr & DCSR_ERROR) { + dev_err(d->slave.dev, "pchan %u: error. DCSR:%02x DDAR:%08x DBSA:%08x DBTA:%08x DBSB:%08x DBTB:%08x\n", + p->num, dcsr, + readl_relaxed(p->base + DMA_DDAR), + readl_relaxed(p->base + DMA_DBSA), + readl_relaxed(p->base + DMA_DBTA), + readl_relaxed(p->base + DMA_DBSB), + readl_relaxed(p->base + DMA_DBTB)); + } + + c = p->vchan; + if (c) { + unsigned long flags; + + spin_lock_irqsave(&c->lock, flags); + /* + * Now that we're holding the lock, check that the vchan + * really is associated with this pchan before touching the + * hardware. This should always succeed, because we won't + * change p->vchan or c->phy while the channel is actively + * transferring. + */ + if (c->phy == p) { + if (dcsr & DCSR_DONEA) + sa11x0_dma_complete(p, c); + if (dcsr & DCSR_DONEB) + sa11x0_dma_complete(p, c); + } + spin_unlock_irqrestore(&c->lock, flags); + } + + return IRQ_HANDLED; +} + +static void sa11x0_dma_start_txd(struct sa11x0_dma_chan *c) +{ + struct sa11x0_dma_desc *txd = sa11x0_dma_next_desc(c); + + /* If the issued list is empty, we have no further txds to process */ + if (txd) { + struct sa11x0_dma_phy *p = c->phy; + + sa11x0_dma_start_desc(p, txd); + p->txd_done = txd; + p->sg_done = 0; + + /* The channel should not have any transfers started */ + WARN_ON(readl_relaxed(p->base + DMA_DCSR_R) & + (DCSR_STRTA | DCSR_STRTB)); + + /* Clear the run and start bits before changing DDAR */ + writel_relaxed(DCSR_RUN | DCSR_STRTA | DCSR_STRTB, + p->base + DMA_DCSR_C); + writel_relaxed(txd->ddar, p->base + DMA_DDAR); + + /* Try to start both buffers */ + sa11x0_dma_start_sg(p, c); + sa11x0_dma_start_sg(p, c); + } +} + +static void sa11x0_dma_tasklet(unsigned long arg) +{ + struct sa11x0_dma_dev *d = (struct sa11x0_dma_dev *)arg; + struct sa11x0_dma_phy *p; + struct sa11x0_dma_chan *c; + struct sa11x0_dma_desc *txd, *txn; + LIST_HEAD(head); + unsigned pch, pch_alloc = 0; + + dev_dbg(d->slave.dev, "tasklet enter\n"); + + /* Get the completed tx descriptors */ + spin_lock_irq(&d->lock); + list_splice_init(&d->desc_complete, &head); + spin_unlock_irq(&d->lock); + + list_for_each_entry(txd, &head, node) { + c = to_sa11x0_dma_chan(txd->tx.chan); + + dev_dbg(d->slave.dev, "vchan %p: txd %p[%x] completed\n", + c, txd, txd->tx.cookie); + + spin_lock_irq(&c->lock); + p = c->phy; + if (p) { + if (!p->txd_done) + sa11x0_dma_start_txd(c); + if (!p->txd_done) { + /* No current txd associated with this channel */ + dev_dbg(d->slave.dev, "pchan %u: free\n", p->num); + + /* Mark this channel free */ + c->phy = NULL; + p->vchan = NULL; + } + } + spin_unlock_irq(&c->lock); + } + + spin_lock_irq(&d->lock); + for (pch = 0; pch < NR_PHY_CHAN; pch++) { + p = &d->phy[pch]; + + if (p->vchan == NULL && !list_empty(&d->chan_pending)) { + c = list_first_entry(&d->chan_pending, + struct sa11x0_dma_chan, node); + list_del_init(&c->node); + + pch_alloc |= 1 << pch; + + /* Mark this channel allocated */ + p->vchan = c; + + dev_dbg(d->slave.dev, "pchan %u: alloc vchan %p\n", pch, c); + } + } + spin_unlock_irq(&d->lock); + + for (pch = 0; pch < NR_PHY_CHAN; pch++) { + if (pch_alloc & (1 << pch)) { + p = &d->phy[pch]; + c = p->vchan; + + spin_lock_irq(&c->lock); + c->phy = p; + + sa11x0_dma_start_txd(c); + spin_unlock_irq(&c->lock); + } + } + + /* Now free the completed tx descriptor, and call their callbacks */ + list_for_each_entry_safe(txd, txn, &head, node) { + dma_async_tx_callback callback = txd->tx.callback; + void *callback_param = txd->tx.callback_param; + + dev_dbg(d->slave.dev, "txd %p[%x]: callback and free\n", + txd, txd->tx.cookie); + + kfree(txd); + + if (callback) + callback(callback_param); + } + + dev_dbg(d->slave.dev, "tasklet exit\n"); +} + + +static void sa11x0_dma_desc_free(struct sa11x0_dma_dev *d, struct list_head *head) +{ + struct sa11x0_dma_desc *txd, *txn; + + list_for_each_entry_safe(txd, txn, head, node) { + dev_dbg(d->slave.dev, "txd %p: freeing\n", txd); + kfree(txd); + } +} + +static int sa11x0_dma_alloc_chan_resources(struct dma_chan *chan) +{ + return 0; +} + +static void sa11x0_dma_free_chan_resources(struct dma_chan *chan) +{ + struct sa11x0_dma_chan *c = to_sa11x0_dma_chan(chan); + struct sa11x0_dma_dev *d = to_sa11x0_dma(chan->device); + unsigned long flags; + LIST_HEAD(head); + + spin_lock_irqsave(&c->lock, flags); + spin_lock(&d->lock); + list_del_init(&c->node); + spin_unlock(&d->lock); + + list_splice_tail_init(&c->desc_submitted, &head); + list_splice_tail_init(&c->desc_issued, &head); + spin_unlock_irqrestore(&c->lock, flags); + + sa11x0_dma_desc_free(d, &head); +} + +static dma_addr_t sa11x0_dma_pos(struct sa11x0_dma_phy *p) +{ + unsigned reg; + u32 dcsr; + + dcsr = readl_relaxed(p->base + DMA_DCSR_R); + + if ((dcsr & (DCSR_BIU | DCSR_STRTA)) == DCSR_STRTA || + (dcsr & (DCSR_BIU | DCSR_STRTB)) == DCSR_BIU) + reg = DMA_DBSA; + else + reg = DMA_DBSB; + + return readl_relaxed(p->base + reg); +} + +static enum dma_status sa11x0_dma_tx_status(struct dma_chan *chan, + dma_cookie_t cookie, struct dma_tx_state *state) +{ + struct sa11x0_dma_chan *c = to_sa11x0_dma_chan(chan); + struct sa11x0_dma_dev *d = to_sa11x0_dma(chan->device); + struct sa11x0_dma_phy *p; + struct sa11x0_dma_desc *txd; + dma_cookie_t last_used, last_complete; + unsigned long flags; + enum dma_status ret; + size_t bytes = 0; + + last_used = c->chan.cookie; + last_complete = c->lc; + + ret = dma_async_is_complete(cookie, last_complete, last_used); + if (ret == DMA_SUCCESS) { + dma_set_tx_state(state, last_complete, last_used, 0); + return ret; + } + + spin_lock_irqsave(&c->lock, flags); + p = c->phy; + ret = c->status; + if (p) { + dma_addr_t addr = sa11x0_dma_pos(p); + + dev_vdbg(d->slave.dev, "tx_status: addr:%x\n", addr); + + txd = p->txd_done; + if (txd) { + unsigned i; + + for (i = 0; i < txd->sglen; i++) { + dev_vdbg(d->slave.dev, "tx_status: [%u] %x+%x\n", + i, txd->sg[i].addr, txd->sg[i].len); + if (addr >= txd->sg[i].addr && + addr < txd->sg[i].addr + txd->sg[i].len) { + unsigned len; + + len = txd->sg[i].len - + (addr - txd->sg[i].addr); + dev_vdbg(d->slave.dev, "tx_status: [%u] +%x\n", + i, len); + bytes += len; + i++; + break; + } + } + for (; i < txd->sglen; i++) { + dev_vdbg(d->slave.dev, "tx_status: [%u] %x+%x ++\n", + i, txd->sg[i].addr, txd->sg[i].len); + bytes += txd->sg[i].len; + } + } + if (txd != p->txd_load && p->txd_load) + bytes += p->txd_load->size; + } + list_for_each_entry(txd, &c->desc_issued, node) { + bytes += txd->size; + } + spin_unlock_irqrestore(&c->lock, flags); + + dma_set_tx_state(state, last_complete, last_used, bytes); + + dev_vdbg(d->slave.dev, "tx_status: bytes 0x%zx\n", bytes); + + return ret; +} + +/* + * Move pending txds to the issued list, and re-init pending list. + * If not already pending, add this channel to the list of pending + * channels and trigger the tasklet to run. + */ +static void sa11x0_dma_issue_pending(struct dma_chan *chan) +{ + struct sa11x0_dma_chan *c = to_sa11x0_dma_chan(chan); + struct sa11x0_dma_dev *d = to_sa11x0_dma(chan->device); + unsigned long flags; + + spin_lock_irqsave(&c->lock, flags); + list_splice_tail_init(&c->desc_submitted, &c->desc_issued); + if (!list_empty(&c->desc_issued)) { + spin_lock(&d->lock); + if (!c->phy && list_empty(&c->node)) { + list_add_tail(&c->node, &d->chan_pending); + tasklet_schedule(&d->task); + dev_dbg(d->slave.dev, "vchan %p: issued\n", c); + } + spin_unlock(&d->lock); + } else + dev_dbg(d->slave.dev, "vchan %p: nothing to issue\n", c); + spin_unlock_irqrestore(&c->lock, flags); +} + +static dma_cookie_t sa11x0_dma_tx_submit(struct dma_async_tx_descriptor *tx) +{ + struct sa11x0_dma_chan *c = to_sa11x0_dma_chan(tx->chan); + struct sa11x0_dma_desc *txd = to_sa11x0_dma_tx(tx); + unsigned long flags; + + spin_lock_irqsave(&c->lock, flags); + c->chan.cookie += 1; + if (c->chan.cookie < 0) + c->chan.cookie = 1; + txd->tx.cookie = c->chan.cookie; + + list_add_tail(&txd->node, &c->desc_submitted); + spin_unlock_irqrestore(&c->lock, flags); + + dev_dbg(tx->chan->device->dev, "vchan %p: txd %p[%x]: submitted\n", + c, txd, txd->tx.cookie); + + return txd->tx.cookie; +} + +static struct dma_async_tx_descriptor *sa11x0_dma_prep_slave_sg( + struct dma_chan *chan, struct scatterlist *sg, unsigned int sglen, + enum dma_transfer_direction dir, unsigned long flags) +{ + struct sa11x0_dma_chan *c = to_sa11x0_dma_chan(chan); + struct sa11x0_dma_desc *txd; + struct scatterlist *sgent; + unsigned i, j = sglen; + size_t size = 0; + + /* SA11x0 channels can only operate in their native direction */ + if (dir != (c->ddar & DDAR_RW ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV)) { + dev_err(chan->device->dev, "vchan %p: bad DMA direction: DDAR:%08x dir:%u\n", + c, c->ddar, dir); + return NULL; + } + + /* Do not allow zero-sized txds */ + if (sglen == 0) + return NULL; + + for_each_sg(sg, sgent, sglen, i) { + dma_addr_t addr = sg_dma_address(sgent); + unsigned int len = sg_dma_len(sgent); + + if (len > DMA_MAX_SIZE) + j += DIV_ROUND_UP(len, DMA_MAX_SIZE & ~DMA_ALIGN) - 1; + if (addr & DMA_ALIGN) { + dev_dbg(chan->device->dev, "vchan %p: bad buffer alignment: %08x\n", + c, addr); + return NULL; + } + } + + txd = kzalloc(sizeof(*txd) + j * sizeof(txd->sg[0]), GFP_ATOMIC); + if (!txd) { + dev_dbg(chan->device->dev, "vchan %p: kzalloc failed\n", c); + return NULL; + } + + j = 0; + for_each_sg(sg, sgent, sglen, i) { + dma_addr_t addr = sg_dma_address(sgent); + unsigned len = sg_dma_len(sgent); + + size += len; + + do { + unsigned tlen = len; + + /* + * Check whether the transfer will fit. If not, try + * to split the transfer up such that we end up with + * equal chunks - but make sure that we preserve the + * alignment. This avoids small segments. + */ + if (tlen > DMA_MAX_SIZE) { + unsigned mult = DIV_ROUND_UP(tlen, + DMA_MAX_SIZE & ~DMA_ALIGN); + + tlen = (tlen / mult) & ~DMA_ALIGN; + } + + txd->sg[j].addr = addr; + txd->sg[j].len = tlen; + + addr += tlen; + len -= tlen; + j++; + } while (len); + } + + dma_async_tx_descriptor_init(&txd->tx, &c->chan); + txd->tx.flags = flags; + txd->tx.tx_submit = sa11x0_dma_tx_submit; + txd->ddar = c->ddar; + txd->size = size; + txd->sglen = j; + + dev_dbg(chan->device->dev, "vchan %p: txd %p: size %u nr %u\n", + c, txd, txd->size, txd->sglen); + + return &txd->tx; +} + +static int sa11x0_dma_slave_config(struct sa11x0_dma_chan *c, struct dma_slave_config *cfg) +{ + u32 ddar = c->ddar & ((0xf << 4) | DDAR_RW); + dma_addr_t addr; + enum dma_slave_buswidth width; + u32 maxburst; + + if (ddar & DDAR_RW) { + addr = cfg->src_addr; + width = cfg->src_addr_width; + maxburst = cfg->src_maxburst; + } else { + addr = cfg->dst_addr; + width = cfg->dst_addr_width; + maxburst = cfg->dst_maxburst; + } + + if ((width != DMA_SLAVE_BUSWIDTH_1_BYTE && + width != DMA_SLAVE_BUSWIDTH_2_BYTES) || + (maxburst != 4 && maxburst != 8)) + return -EINVAL; + + if (width == DMA_SLAVE_BUSWIDTH_2_BYTES) + ddar |= DDAR_DW; + if (maxburst == 8) + ddar |= DDAR_BS; + + dev_dbg(c->chan.device->dev, "vchan %p: dma_slave_config addr %x width %u burst %u\n", + c, addr, width, maxburst); + + c->ddar = ddar | (addr & 0xf0000000) | (addr & 0x003ffffc) << 6; + + return 0; +} + +static int sa11x0_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, + unsigned long arg) +{ + struct sa11x0_dma_chan *c = to_sa11x0_dma_chan(chan); + struct sa11x0_dma_dev *d = to_sa11x0_dma(chan->device); + struct sa11x0_dma_phy *p; + LIST_HEAD(head); + unsigned long flags; + int ret; + + switch (cmd) { + case DMA_SLAVE_CONFIG: + return sa11x0_dma_slave_config(c, (struct dma_slave_config *)arg); + + case DMA_TERMINATE_ALL: + dev_dbg(d->slave.dev, "vchan %p: terminate all\n", c); + /* Clear the tx descriptor lists */ + spin_lock_irqsave(&c->lock, flags); + list_splice_tail_init(&c->desc_submitted, &head); + list_splice_tail_init(&c->desc_issued, &head); + + p = c->phy; + if (p) { + struct sa11x0_dma_desc *txd, *txn; + + dev_dbg(d->slave.dev, "pchan %u: terminating\n", p->num); + /* vchan is assigned to a pchan - stop the channel */ + writel(DCSR_RUN | DCSR_IE | + DCSR_STRTA | DCSR_DONEA | + DCSR_STRTB | DCSR_DONEB, + p->base + DMA_DCSR_C); + + list_for_each_entry_safe(txd, txn, &d->desc_complete, node) + if (txd->tx.chan == &c->chan) + list_move(&txd->node, &head); + + if (p->txd_load) { + if (p->txd_load != p->txd_done) + list_add_tail(&p->txd_load->node, &head); + p->txd_load = NULL; + } + if (p->txd_done) { + list_add_tail(&p->txd_done->node, &head); + p->txd_done = NULL; + } + c->phy = NULL; + spin_lock(&d->lock); + p->vchan = NULL; + spin_unlock(&d->lock); + tasklet_schedule(&d->task); + } + spin_unlock_irqrestore(&c->lock, flags); + sa11x0_dma_desc_free(d, &head); + ret = 0; + break; + + case DMA_PAUSE: + dev_dbg(d->slave.dev, "vchan %p: pause\n", c); + spin_lock_irqsave(&c->lock, flags); + if (c->status == DMA_IN_PROGRESS) { + c->status = DMA_PAUSED; + + p = c->phy; + if (p) { + writel(DCSR_RUN | DCSR_IE, p->base + DMA_DCSR_C); + } else { + spin_lock(&d->lock); + list_del_init(&c->node); + spin_unlock(&d->lock); + } + } + spin_unlock_irqrestore(&c->lock, flags); + ret = 0; + break; + + case DMA_RESUME: + dev_dbg(d->slave.dev, "vchan %p: resume\n", c); + spin_lock_irqsave(&c->lock, flags); + if (c->status == DMA_PAUSED) { + c->status = DMA_IN_PROGRESS; + + p = c->phy; + if (p) { + writel(DCSR_RUN | DCSR_IE, p->base + DMA_DCSR_S); + } else if (!list_empty(&c->desc_issued)) { + spin_lock(&d->lock); + list_add_tail(&c->node, &d->chan_pending); + spin_unlock(&d->lock); + } + } + spin_unlock_irqrestore(&c->lock, flags); + ret = 0; + break; + + default: + ret = -ENXIO; + break; + } + + return ret; +} + +struct sa11x0_dma_channel_desc { + u32 ddar; + const char *name; +}; + +#define CD(d1, d2) { .ddar = DDAR_##d1 | d2, .name = #d1 } +static const struct sa11x0_dma_channel_desc chan_desc[] = { + CD(Ser0UDCTr, 0), + CD(Ser0UDCRc, DDAR_RW), + CD(Ser1SDLCTr, 0), + CD(Ser1SDLCRc, DDAR_RW), + CD(Ser1UARTTr, 0), + CD(Ser1UARTRc, DDAR_RW), + CD(Ser2ICPTr, 0), + CD(Ser2ICPRc, DDAR_RW), + CD(Ser3UARTTr, 0), + CD(Ser3UARTRc, DDAR_RW), + CD(Ser4MCP0Tr, 0), + CD(Ser4MCP0Rc, DDAR_RW), + CD(Ser4MCP1Tr, 0), + CD(Ser4MCP1Rc, DDAR_RW), + CD(Ser4SSPTr, 0), + CD(Ser4SSPRc, DDAR_RW), +}; + +static int __devinit sa11x0_dma_init_dmadev(struct dma_device *dmadev, + struct device *dev) +{ + unsigned i; + + dmadev->chancnt = ARRAY_SIZE(chan_desc); + INIT_LIST_HEAD(&dmadev->channels); + dmadev->dev = dev; + dmadev->device_alloc_chan_resources = sa11x0_dma_alloc_chan_resources; + dmadev->device_free_chan_resources = sa11x0_dma_free_chan_resources; + dmadev->device_control = sa11x0_dma_control; + dmadev->device_tx_status = sa11x0_dma_tx_status; + dmadev->device_issue_pending = sa11x0_dma_issue_pending; + + for (i = 0; i < dmadev->chancnt; i++) { + struct sa11x0_dma_chan *c; + + c = kzalloc(sizeof(*c), GFP_KERNEL); + if (!c) { + dev_err(dev, "no memory for channel %u\n", i); + return -ENOMEM; + } + + c->chan.device = dmadev; + c->status = DMA_IN_PROGRESS; + c->ddar = chan_desc[i].ddar; + c->name = chan_desc[i].name; + spin_lock_init(&c->lock); + INIT_LIST_HEAD(&c->desc_submitted); + INIT_LIST_HEAD(&c->desc_issued); + INIT_LIST_HEAD(&c->node); + list_add_tail(&c->chan.device_node, &dmadev->channels); + } + + return dma_async_device_register(dmadev); +} + +static int sa11x0_dma_request_irq(struct platform_device *pdev, int nr, + void *data) +{ + int irq = platform_get_irq(pdev, nr); + + if (irq <= 0) + return -ENXIO; + + return request_irq(irq, sa11x0_dma_irq, 0, dev_name(&pdev->dev), data); +} + +static void sa11x0_dma_free_irq(struct platform_device *pdev, int nr, + void *data) +{ + int irq = platform_get_irq(pdev, nr); + if (irq > 0) + free_irq(irq, data); +} + +static void sa11x0_dma_free_channels(struct dma_device *dmadev) +{ + struct sa11x0_dma_chan *c, *cn; + + list_for_each_entry_safe(c, cn, &dmadev->channels, chan.device_node) { + list_del(&c->chan.device_node); + kfree(c); + } +} + +static int __devinit sa11x0_dma_probe(struct platform_device *pdev) +{ + struct sa11x0_dma_dev *d; + struct resource *res; + unsigned i; + int ret; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENXIO; + + d = kzalloc(sizeof(*d), GFP_KERNEL); + if (!d) { + ret = -ENOMEM; + goto err_alloc; + } + + spin_lock_init(&d->lock); + INIT_LIST_HEAD(&d->chan_pending); + INIT_LIST_HEAD(&d->desc_complete); + + d->base = ioremap(res->start, resource_size(res)); + if (!d->base) { + ret = -ENOMEM; + goto err_ioremap; + } + + tasklet_init(&d->task, sa11x0_dma_tasklet, (unsigned long)d); + + for (i = 0; i < NR_PHY_CHAN; i++) { + struct sa11x0_dma_phy *p = &d->phy[i]; + + p->dev = d; + p->num = i; + p->base = d->base + i * DMA_SIZE; + writel_relaxed(DCSR_RUN | DCSR_IE | DCSR_ERROR | + DCSR_DONEA | DCSR_STRTA | DCSR_DONEB | DCSR_STRTB, + p->base + DMA_DCSR_C); + writel_relaxed(0, p->base + DMA_DDAR); + + ret = sa11x0_dma_request_irq(pdev, i, p); + if (ret) { + while (i) { + i--; + sa11x0_dma_free_irq(pdev, i, &d->phy[i]); + } + goto err_irq; + } + } + + dma_cap_set(DMA_SLAVE, d->slave.cap_mask); + d->slave.device_prep_slave_sg = sa11x0_dma_prep_slave_sg; + ret = sa11x0_dma_init_dmadev(&d->slave, &pdev->dev); + if (ret) { + dev_warn(d->slave.dev, "failed to register slave async device: %d\n", + ret); + goto err_slave_reg; + } + + platform_set_drvdata(pdev, d); + return 0; + + err_slave_reg: + sa11x0_dma_free_channels(&d->slave); + for (i = 0; i < NR_PHY_CHAN; i++) + sa11x0_dma_free_irq(pdev, i, &d->phy[i]); + err_irq: + tasklet_kill(&d->task); + iounmap(d->base); + err_ioremap: + kfree(d); + err_alloc: + return ret; +} + +static int __devexit sa11x0_dma_remove(struct platform_device *pdev) +{ + struct sa11x0_dma_dev *d = platform_get_drvdata(pdev); + unsigned pch; + + dma_async_device_unregister(&d->slave); + + sa11x0_dma_free_channels(&d->slave); + for (pch = 0; pch < NR_PHY_CHAN; pch++) + sa11x0_dma_free_irq(pdev, pch, &d->phy[pch]); + tasklet_kill(&d->task); + iounmap(d->base); + kfree(d); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int sa11x0_dma_suspend(struct device *dev) +{ + struct sa11x0_dma_dev *d = dev_get_drvdata(dev); + unsigned pch; + + for (pch = 0; pch < NR_PHY_CHAN; pch++) { + struct sa11x0_dma_phy *p = &d->phy[pch]; + u32 dcsr, saved_dcsr; + + dcsr = saved_dcsr = readl_relaxed(p->base + DMA_DCSR_R); + if (dcsr & DCSR_RUN) { + writel(DCSR_RUN | DCSR_IE, p->base + DMA_DCSR_C); + dcsr = readl_relaxed(p->base + DMA_DCSR_R); + } + + saved_dcsr &= DCSR_RUN | DCSR_IE; + if (dcsr & DCSR_BIU) { + p->dbs[0] = readl_relaxed(p->base + DMA_DBSB); + p->dbt[0] = readl_relaxed(p->base + DMA_DBTB); + p->dbs[1] = readl_relaxed(p->base + DMA_DBSA); + p->dbt[1] = readl_relaxed(p->base + DMA_DBTA); + saved_dcsr |= (dcsr & DCSR_STRTA ? DCSR_STRTB : 0) | + (dcsr & DCSR_STRTB ? DCSR_STRTA : 0); + } else { + p->dbs[0] = readl_relaxed(p->base + DMA_DBSA); + p->dbt[0] = readl_relaxed(p->base + DMA_DBTA); + p->dbs[1] = readl_relaxed(p->base + DMA_DBSB); + p->dbt[1] = readl_relaxed(p->base + DMA_DBTB); + saved_dcsr |= dcsr & (DCSR_STRTA | DCSR_STRTB); + } + p->dcsr = saved_dcsr; + + writel(DCSR_STRTA | DCSR_STRTB, p->base + DMA_DCSR_C); + } + + return 0; +} + +static int sa11x0_dma_resume(struct device *dev) +{ + struct sa11x0_dma_dev *d = dev_get_drvdata(dev); + unsigned pch; + + for (pch = 0; pch < NR_PHY_CHAN; pch++) { + struct sa11x0_dma_phy *p = &d->phy[pch]; + struct sa11x0_dma_desc *txd = NULL; + u32 dcsr = readl_relaxed(p->base + DMA_DCSR_R); + + WARN_ON(dcsr & (DCSR_BIU | DCSR_STRTA | DCSR_STRTB | DCSR_RUN)); + + if (p->txd_done) + txd = p->txd_done; + else if (p->txd_load) + txd = p->txd_load; + + if (!txd) + continue; + + writel_relaxed(txd->ddar, p->base + DMA_DDAR); + + writel_relaxed(p->dbs[0], p->base + DMA_DBSA); + writel_relaxed(p->dbt[0], p->base + DMA_DBTA); + writel_relaxed(p->dbs[1], p->base + DMA_DBSB); + writel_relaxed(p->dbt[1], p->base + DMA_DBTB); + writel_relaxed(p->dcsr, p->base + DMA_DCSR_S); + } + + return 0; +} +#endif + +static const struct dev_pm_ops sa11x0_dma_pm_ops = { + .suspend_noirq = sa11x0_dma_suspend, + .resume_noirq = sa11x0_dma_resume, + .freeze_noirq = sa11x0_dma_suspend, + .thaw_noirq = sa11x0_dma_resume, + .poweroff_noirq = sa11x0_dma_suspend, + .restore_noirq = sa11x0_dma_resume, +}; + +static struct platform_driver sa11x0_dma_driver = { + .driver = { + .name = "sa11x0-dma", + .owner = THIS_MODULE, + .pm = &sa11x0_dma_pm_ops, + }, + .probe = sa11x0_dma_probe, + .remove = __devexit_p(sa11x0_dma_remove), +}; + +bool sa11x0_dma_filter_fn(struct dma_chan *chan, void *param) +{ + if (chan->device->dev->driver == &sa11x0_dma_driver.driver) { + struct sa11x0_dma_chan *c = to_sa11x0_dma_chan(chan); + const char *p = param; + + return !strcmp(c->name, p); + } + return false; +} +EXPORT_SYMBOL(sa11x0_dma_filter_fn); + +static int __init sa11x0_dma_init(void) +{ + return platform_driver_register(&sa11x0_dma_driver); +} +subsys_initcall(sa11x0_dma_init); + +static void __exit sa11x0_dma_exit(void) +{ + platform_driver_unregister(&sa11x0_dma_driver); +} +module_exit(sa11x0_dma_exit); + +MODULE_AUTHOR("Russell King"); +MODULE_DESCRIPTION("SA-11x0 DMA driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:sa11x0-dma"); diff --git a/include/linux/sa11x0-dma.h b/include/linux/sa11x0-dma.h new file mode 100644 index 000000000000..65839a58b8e5 --- /dev/null +++ b/include/linux/sa11x0-dma.h @@ -0,0 +1,24 @@ +/* + * SA11x0 DMA Engine support + * + * Copyright (C) 2012 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __LINUX_SA11X0_DMA_H +#define __LINUX_SA11X0_DMA_H + +struct dma_chan; + +#if defined(CONFIG_DMA_SA11X0) || defined(CONFIG_DMA_SA11X0_MODULE) +bool sa11x0_dma_filter_fn(struct dma_chan *, void *); +#else +static inline bool sa11x0_dma_filter_fn(struct dma_chan *c, void *d) +{ + return false; +} +#endif + +#endif -- cgit v1.2.3 From d8e0539ebdff5ff27fa7eb019715d9dfb5171a3b Mon Sep 17 00:00:00 2001 From: Weston Andros Adamson Date: Tue, 6 Mar 2012 20:46:43 -0500 Subject: NFS: add filehandle crc for debug display Match wireshark's CRC-32 hash for easier debugging Signed-off-by: Weston Andros Adamson Signed-off-by: Trond Myklebust --- fs/nfs/inode.c | 23 ++++++++++++++++++++--- include/linux/nfs_fs.h | 8 ++++++++ 2 files changed, 28 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index ba03b7908149..0337554f1561 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -39,6 +39,7 @@ #include #include #include +#include #include #include @@ -1045,7 +1046,23 @@ struct nfs_fh *nfs_alloc_fhandle(void) return fh; } -/** +#ifdef RPC_DEBUG +/* + * _nfs_display_fhandle_hash - calculate the crc32 hash for the filehandle + * in the same way that wireshark does + * + * @fh: file handle + * + * For debugging only. + */ +u32 _nfs_display_fhandle_hash(const struct nfs_fh *fh) +{ + /* wireshark uses 32-bit AUTODIN crc and does a bitwise + * not on the result */ + return ~crc32(0xFFFFFFFF, &fh->data[0], fh->size); +} + +/* * _nfs_display_fhandle - display an NFS file handle on the console * * @fh: file handle to display @@ -1053,7 +1070,6 @@ struct nfs_fh *nfs_alloc_fhandle(void) * * For debugging only. */ -#ifdef RPC_DEBUG void _nfs_display_fhandle(const struct nfs_fh *fh, const char *caption) { unsigned short i; @@ -1063,7 +1079,8 @@ void _nfs_display_fhandle(const struct nfs_fh *fh, const char *caption) return; } - printk(KERN_DEFAULT "%s at %p is %u bytes:\n", caption, fh, fh->size); + printk(KERN_DEFAULT "%s at %p is %u bytes, crc: 0x%08x:\n", + caption, fh, fh->size, _nfs_display_fhandle_hash(fh)); for (i = 0; i < fh->size; i += 16) { __be32 *pos = (__be32 *)&fh->data[i]; diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index c07a757649dc..ce8e4361ad14 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -396,6 +396,11 @@ static inline void nfs_free_fhandle(const struct nfs_fh *fh) } #ifdef RPC_DEBUG +extern u32 _nfs_display_fhandle_hash(const struct nfs_fh *fh); +static inline u32 nfs_display_fhandle_hash(const struct nfs_fh *fh) +{ + return _nfs_display_fhandle_hash(fh); +} extern void _nfs_display_fhandle(const struct nfs_fh *fh, const char *caption); #define nfs_display_fhandle(fh, caption) \ do { \ @@ -403,6 +408,9 @@ extern void _nfs_display_fhandle(const struct nfs_fh *fh, const char *caption); _nfs_display_fhandle(fh, caption); \ } while (0) #else +static inline u32 nfs_display_fhandle_hash(const struct nfs_fh *fh) +{ +} static inline void nfs_display_fhandle(const struct nfs_fh *fh, const char *caption) { -- cgit v1.2.3 From 9994b62b5621f88828d442fcd03fe3ce4c43344b Mon Sep 17 00:00:00 2001 From: Fred Isaman Date: Thu, 8 Mar 2012 17:29:34 -0500 Subject: NFS: remove NFS_PAGE_TAG_LOCKED The last real use of this tag was removed by commit 7f2f12d963 NFS: Simplify nfs_wb_page() Signed-off-by: Fred Isaman Signed-off-by: Trond Myklebust --- fs/nfs/pagelist.c | 32 +------------------------------- fs/nfs/write.c | 20 +++++++++----------- include/linux/nfs_page.h | 3 --- 3 files changed, 10 insertions(+), 45 deletions(-) (limited to 'include/linux') diff --git a/fs/nfs/pagelist.c b/fs/nfs/pagelist.c index 77a184e2fe47..fc5b54b84f8f 100644 --- a/fs/nfs/pagelist.c +++ b/fs/nfs/pagelist.c @@ -107,36 +107,6 @@ void nfs_unlock_request(struct nfs_page *req) nfs_release_request(req); } -/** - * nfs_set_page_tag_locked - Tag a request as locked - * @req: - */ -int nfs_set_page_tag_locked(struct nfs_page *req) -{ - if (!nfs_lock_request_dontget(req)) - return 0; - if (test_bit(PG_MAPPED, &req->wb_flags)) - radix_tree_tag_set(&NFS_I(req->wb_context->dentry->d_inode)->nfs_page_tree, req->wb_index, NFS_PAGE_TAG_LOCKED); - return 1; -} - -/** - * nfs_clear_page_tag_locked - Clear request tag and wake up sleepers - */ -void nfs_clear_page_tag_locked(struct nfs_page *req) -{ - if (test_bit(PG_MAPPED, &req->wb_flags)) { - struct inode *inode = req->wb_context->dentry->d_inode; - struct nfs_inode *nfsi = NFS_I(inode); - - spin_lock(&inode->i_lock); - radix_tree_tag_clear(&nfsi->nfs_page_tree, req->wb_index, NFS_PAGE_TAG_LOCKED); - nfs_unlock_request(req); - spin_unlock(&inode->i_lock); - } else - nfs_unlock_request(req); -} - /* * nfs_clear_request - Free up all resources allocated to the request * @req: @@ -469,7 +439,7 @@ int nfs_scan_list(struct nfs_inode *nfsi, if (req->wb_index > idx_end) goto out; idx_start = req->wb_index + 1; - if (nfs_set_page_tag_locked(req)) { + if (nfs_lock_request_dontget(req)) { kref_get(&req->wb_kref); radix_tree_tag_clear(&nfsi->nfs_page_tree, req->wb_index, tag); diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 0b1831d95849..fd8a4f07bc0c 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -236,10 +236,10 @@ static struct nfs_page *nfs_find_and_lock_request(struct page *page, bool nonblo req = nfs_page_find_request_locked(page); if (req == NULL) break; - if (nfs_set_page_tag_locked(req)) + if (nfs_lock_request_dontget(req)) break; /* Note: If we hold the page lock, as is the case in nfs_writepage, - * then the call to nfs_set_page_tag_locked() will always + * then the call to nfs_lock_request_dontget() will always * succeed provided that someone hasn't already marked the * request as dirty (in which case we don't care). */ @@ -397,8 +397,6 @@ static int nfs_inode_add_request(struct inode *inode, struct nfs_page *req) set_page_private(req->wb_page, (unsigned long)req); nfsi->npages++; kref_get(&req->wb_kref); - radix_tree_tag_set(&nfsi->nfs_page_tree, req->wb_index, - NFS_PAGE_TAG_LOCKED); spin_unlock(&inode->i_lock); radix_tree_preload_end(); out: @@ -604,7 +602,7 @@ static struct nfs_page *nfs_try_to_update_request(struct inode *inode, || end < req->wb_offset) goto out_flushme; - if (nfs_set_page_tag_locked(req)) + if (nfs_lock_request_dontget(req)) break; /* The request is locked, so wait and then retry */ @@ -684,7 +682,7 @@ static int nfs_writepage_setup(struct nfs_open_context *ctx, struct page *page, nfs_grow_file(page, offset, count); nfs_mark_uptodate(page, req->wb_pgbase, req->wb_bytes); nfs_mark_request_dirty(req); - nfs_clear_page_tag_locked(req); + nfs_unlock_request(req); return 0; } @@ -777,7 +775,7 @@ static void nfs_writepage_release(struct nfs_page *req, if (PageError(req->wb_page) || !nfs_reschedule_unstable_write(req, data)) nfs_inode_remove_request(req); - nfs_clear_page_tag_locked(req); + nfs_unlock_request(req); nfs_end_page_writeback(page); } @@ -925,7 +923,7 @@ static void nfs_redirty_request(struct nfs_page *req) struct page *page = req->wb_page; nfs_mark_request_dirty(req); - nfs_clear_page_tag_locked(req); + nfs_unlock_request(req); nfs_end_page_writeback(page); } @@ -1199,7 +1197,7 @@ static void nfs_writeback_release_full(void *calldata) remove_request: nfs_inode_remove_request(req); next: - nfs_clear_page_tag_locked(req); + nfs_unlock_request(req); nfs_end_page_writeback(page); } nfs_writedata_release(calldata); @@ -1411,7 +1409,7 @@ void nfs_retry_commit(struct list_head *page_list, dec_zone_page_state(req->wb_page, NR_UNSTABLE_NFS); dec_bdi_stat(req->wb_page->mapping->backing_dev_info, BDI_RECLAIMABLE); - nfs_clear_page_tag_locked(req); + nfs_unlock_request(req); } } EXPORT_SYMBOL_GPL(nfs_retry_commit); @@ -1486,7 +1484,7 @@ void nfs_commit_release_pages(struct nfs_write_data *data) dprintk(" mismatch\n"); nfs_mark_request_dirty(req); next: - nfs_clear_page_tag_locked(req); + nfs_unlock_request(req); } } EXPORT_SYMBOL_GPL(nfs_commit_release_pages); diff --git a/include/linux/nfs_page.h b/include/linux/nfs_page.h index ab465fe8c3d6..65b563f0903a 100644 --- a/include/linux/nfs_page.h +++ b/include/linux/nfs_page.h @@ -21,7 +21,6 @@ /* * Valid flags for the radix tree */ -#define NFS_PAGE_TAG_LOCKED 0 #define NFS_PAGE_TAG_COMMIT 1 /* @@ -106,8 +105,6 @@ extern bool nfs_generic_pg_test(struct nfs_pageio_descriptor *desc, struct nfs_page *req); extern int nfs_wait_on_request(struct nfs_page *); extern void nfs_unlock_request(struct nfs_page *req); -extern int nfs_set_page_tag_locked(struct nfs_page *req); -extern void nfs_clear_page_tag_locked(struct nfs_page *req); /* * Lock the page of an asynchronous request without getting a new reference -- cgit v1.2.3 From d6d6dc7cdfda7c8f49a89a7b7261846f319da6d1 Mon Sep 17 00:00:00 2001 From: Fred Isaman Date: Thu, 8 Mar 2012 17:29:35 -0500 Subject: NFS: remove nfs_inode radix tree The radix tree is only being used to compile lists of reqs needing commit. It is simpler to just put the reqs directly into a list. Signed-off-by: Fred Isaman Signed-off-by: Trond Myklebust --- fs/nfs/inode.c | 2 +- fs/nfs/internal.h | 2 + fs/nfs/nfs4filelayout.c | 109 +++++++++++++++++++++++++++++++++--------- fs/nfs/nfs4filelayout.h | 7 ++- fs/nfs/pagelist.c | 61 ------------------------ fs/nfs/pnfs.h | 82 ++++++++++++++++---------------- fs/nfs/write.c | 120 ++++++++++++++++++++++++++--------------------- include/linux/nfs_fs.h | 6 +-- include/linux/nfs_page.h | 13 +---- 9 files changed, 208 insertions(+), 194 deletions(-) (limited to 'include/linux') diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 70e25c9c5670..1a19f8d30c14 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -1560,7 +1560,7 @@ static void init_once(void *foo) INIT_LIST_HEAD(&nfsi->open_files); INIT_LIST_HEAD(&nfsi->access_cache_entry_lru); INIT_LIST_HEAD(&nfsi->access_cache_inode_lru); - INIT_RADIX_TREE(&nfsi->nfs_page_tree, GFP_ATOMIC); + INIT_LIST_HEAD(&nfsi->commit_list); nfsi->npages = 0; nfsi->ncommit = 0; atomic_set(&nfsi->silly_count, 1); diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index 0c3648a947d1..04a914704e7b 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -308,6 +308,8 @@ extern void nfs_pageio_reset_read_mds(struct nfs_pageio_descriptor *pgio); extern void nfs_readdata_release(struct nfs_read_data *rdata); /* write.c */ +extern int nfs_scan_commit_list(struct list_head *src, struct list_head *dst, + int max); extern int nfs_generic_flush(struct nfs_pageio_descriptor *desc, struct list_head *head); extern void nfs_pageio_init_write_mds(struct nfs_pageio_descriptor *pgio, diff --git a/fs/nfs/nfs4filelayout.c b/fs/nfs/nfs4filelayout.c index 768f6f86c9f0..716fac6bc082 100644 --- a/fs/nfs/nfs4filelayout.c +++ b/fs/nfs/nfs4filelayout.c @@ -682,14 +682,16 @@ filelayout_alloc_lseg(struct pnfs_layout_hdr *layoutid, int size = (fl->stripe_type == STRIPE_SPARSE) ? fl->dsaddr->ds_num : fl->dsaddr->stripe_count; - fl->commit_buckets = kcalloc(size, sizeof(struct list_head), gfp_flags); + fl->commit_buckets = kcalloc(size, sizeof(struct nfs4_fl_commit_bucket), gfp_flags); if (!fl->commit_buckets) { filelayout_free_lseg(&fl->generic_hdr); return NULL; } fl->number_of_buckets = size; - for (i = 0; i < size; i++) - INIT_LIST_HEAD(&fl->commit_buckets[i]); + for (i = 0; i < size; i++) { + INIT_LIST_HEAD(&fl->commit_buckets[i].written); + INIT_LIST_HEAD(&fl->commit_buckets[i].committing); + } } return &fl->generic_hdr; } @@ -767,11 +769,6 @@ static const struct nfs_pageio_ops filelayout_pg_write_ops = { .pg_doio = pnfs_generic_pg_writepages, }; -static bool filelayout_mark_pnfs_commit(struct pnfs_layout_segment *lseg) -{ - return !FILELAYOUT_LSEG(lseg)->commit_through_mds; -} - static u32 select_bucket_index(struct nfs4_filelayout_segment *fl, u32 j) { if (fl->stripe_type == STRIPE_SPARSE) @@ -780,13 +777,39 @@ static u32 select_bucket_index(struct nfs4_filelayout_segment *fl, u32 j) return j; } -struct list_head *filelayout_choose_commit_list(struct nfs_page *req) +/* The generic layer is about to remove the req from the commit list. + * If this will make the bucket empty, it will need to put the lseg reference. + * Note inode lock is held, so we can't do the put here. + */ +static struct pnfs_layout_segment * +filelayout_remove_commit_req(struct nfs_page *req) +{ + if (list_is_singular(&req->wb_list)) { + struct inode *inode = req->wb_context->dentry->d_inode; + struct pnfs_layout_segment *lseg; + + /* From here we can find the bucket, but for the moment, + * since there is only one relevant lseg... + */ + list_for_each_entry(lseg, &NFS_I(inode)->layout->plh_segs, pls_list) { + if (lseg->pls_range.iomode == IOMODE_RW) + return lseg; + } + } + return NULL; +} + +static struct list_head * +filelayout_choose_commit_list(struct nfs_page *req, + struct pnfs_layout_segment *lseg) { - struct pnfs_layout_segment *lseg = req->wb_commit_lseg; struct nfs4_filelayout_segment *fl = FILELAYOUT_LSEG(lseg); u32 i, j; struct list_head *list; + if (fl->commit_through_mds) + return &NFS_I(req->wb_context->dentry->d_inode)->commit_list; + /* Note that we are calling nfs4_fl_calc_j_index on each page * that ends up being committed to a data server. An attractive * alternative is to add a field to nfs_write_data and nfs_page @@ -796,9 +819,14 @@ struct list_head *filelayout_choose_commit_list(struct nfs_page *req) j = nfs4_fl_calc_j_index(lseg, (loff_t)req->wb_index << PAGE_CACHE_SHIFT); i = select_bucket_index(fl, j); - list = &fl->commit_buckets[i]; + list = &fl->commit_buckets[i].written; if (list_empty(list)) { - /* Non-empty buckets hold a reference on the lseg */ + /* Non-empty buckets hold a reference on the lseg. That ref + * is normally transferred to the COMMIT call and released + * there. It could also be released if the last req is pulled + * off due to a rewrite, in which case it will be done in + * filelayout_remove_commit_req + */ get_lseg(lseg); } return list; @@ -860,18 +888,56 @@ static int filelayout_initiate_commit(struct nfs_write_data *data, int how) /* * This is only useful while we are using whole file layouts. */ -static struct pnfs_layout_segment *find_only_write_lseg(struct inode *inode) +static struct pnfs_layout_segment * +find_only_write_lseg_locked(struct inode *inode) { - struct pnfs_layout_segment *lseg, *rv = NULL; + struct pnfs_layout_segment *lseg; - spin_lock(&inode->i_lock); list_for_each_entry(lseg, &NFS_I(inode)->layout->plh_segs, pls_list) if (lseg->pls_range.iomode == IOMODE_RW) - rv = get_lseg(lseg); + return get_lseg(lseg); + return NULL; +} + +static struct pnfs_layout_segment *find_only_write_lseg(struct inode *inode) +{ + struct pnfs_layout_segment *rv; + + spin_lock(&inode->i_lock); + rv = find_only_write_lseg_locked(inode); spin_unlock(&inode->i_lock); return rv; } +/* Move reqs from written to committing lists, returning count of number moved. + * Note called with i_lock held. + */ +static int filelayout_scan_commit_lists(struct inode *inode, int max) +{ + struct pnfs_layout_segment *lseg; + struct nfs4_filelayout_segment *fl; + int i, rv = 0, cnt; + + lseg = find_only_write_lseg_locked(inode); + if (!lseg) + return 0; + fl = FILELAYOUT_LSEG(lseg); + if (fl->commit_through_mds) + goto out_put; + for (i = 0; i < fl->number_of_buckets; i++) { + if (list_empty(&fl->commit_buckets[i].written)) + continue; + cnt = nfs_scan_commit_list(&fl->commit_buckets[i].written, + &fl->commit_buckets[i].committing, + max); + max -= cnt; + rv += cnt; + } +out_put: + put_lseg(lseg); + return rv; +} + static int alloc_ds_commits(struct inode *inode, struct list_head *list) { struct pnfs_layout_segment *lseg; @@ -886,7 +952,7 @@ static int alloc_ds_commits(struct inode *inode, struct list_head *list) return 0; fl = FILELAYOUT_LSEG(lseg); for (i = 0; i < fl->number_of_buckets; i++) { - if (list_empty(&fl->commit_buckets[i])) + if (list_empty(&fl->commit_buckets[i].committing)) continue; data = nfs_commitdata_alloc(); if (!data) @@ -900,9 +966,9 @@ static int alloc_ds_commits(struct inode *inode, struct list_head *list) out_bad: for (j = i; j < fl->number_of_buckets; j++) { - if (list_empty(&fl->commit_buckets[i])) + if (list_empty(&fl->commit_buckets[i].committing)) continue; - nfs_retry_commit(&fl->commit_buckets[i], lseg); + nfs_retry_commit(&fl->commit_buckets[i].committing, lseg); put_lseg(lseg); /* associated with emptying bucket */ } put_lseg(lseg); @@ -937,7 +1003,7 @@ filelayout_commit_pagelist(struct inode *inode, struct list_head *mds_pages, nfs_initiate_commit(data, NFS_CLIENT(inode), data->mds_ops, how); } else { - nfs_init_commit(data, &FILELAYOUT_LSEG(data->lseg)->commit_buckets[data->ds_commit_index], data->lseg); + nfs_init_commit(data, &FILELAYOUT_LSEG(data->lseg)->commit_buckets[data->ds_commit_index].committing, data->lseg); filelayout_initiate_commit(data, how); } } @@ -967,8 +1033,9 @@ static struct pnfs_layoutdriver_type filelayout_type = { .free_lseg = filelayout_free_lseg, .pg_read_ops = &filelayout_pg_read_ops, .pg_write_ops = &filelayout_pg_write_ops, - .mark_pnfs_commit = filelayout_mark_pnfs_commit, .choose_commit_list = filelayout_choose_commit_list, + .remove_commit_req = filelayout_remove_commit_req, + .scan_commit_lists = filelayout_scan_commit_lists, .commit_pagelist = filelayout_commit_pagelist, .read_pagelist = filelayout_read_pagelist, .write_pagelist = filelayout_write_pagelist, diff --git a/fs/nfs/nfs4filelayout.h b/fs/nfs/nfs4filelayout.h index 2e42284253fa..21190bb1f5e3 100644 --- a/fs/nfs/nfs4filelayout.h +++ b/fs/nfs/nfs4filelayout.h @@ -74,6 +74,11 @@ struct nfs4_file_layout_dsaddr { struct nfs4_pnfs_ds *ds_list[1]; }; +struct nfs4_fl_commit_bucket { + struct list_head written; + struct list_head committing; +}; + struct nfs4_filelayout_segment { struct pnfs_layout_segment generic_hdr; u32 stripe_type; @@ -84,7 +89,7 @@ struct nfs4_filelayout_segment { struct nfs4_file_layout_dsaddr *dsaddr; /* Point to GETDEVINFO data */ unsigned int num_fh; struct nfs_fh **fh_array; - struct list_head *commit_buckets; /* Sort commits to ds */ + struct nfs4_fl_commit_bucket *commit_buckets; /* Sort commits to ds */ int number_of_buckets; }; diff --git a/fs/nfs/pagelist.c b/fs/nfs/pagelist.c index fc5b54b84f8f..d21fceaa9f62 100644 --- a/fs/nfs/pagelist.c +++ b/fs/nfs/pagelist.c @@ -396,67 +396,6 @@ void nfs_pageio_cond_complete(struct nfs_pageio_descriptor *desc, pgoff_t index) } } -#define NFS_SCAN_MAXENTRIES 16 -/** - * nfs_scan_list - Scan a list for matching requests - * @nfsi: NFS inode - * @dst: Destination list - * @idx_start: lower bound of page->index to scan - * @npages: idx_start + npages sets the upper bound to scan. - * @tag: tag to scan for - * - * Moves elements from one of the inode request lists. - * If the number of requests is set to 0, the entire address_space - * starting at index idx_start, is scanned. - * The requests are *not* checked to ensure that they form a contiguous set. - * You must be holding the inode's i_lock when calling this function - */ -int nfs_scan_list(struct nfs_inode *nfsi, - struct list_head *dst, pgoff_t idx_start, - unsigned int npages, int tag) -{ - struct nfs_page *pgvec[NFS_SCAN_MAXENTRIES]; - struct nfs_page *req; - pgoff_t idx_end; - int found, i; - int res; - struct list_head *list; - - res = 0; - if (npages == 0) - idx_end = ~0; - else - idx_end = idx_start + npages - 1; - - for (;;) { - found = radix_tree_gang_lookup_tag(&nfsi->nfs_page_tree, - (void **)&pgvec[0], idx_start, - NFS_SCAN_MAXENTRIES, tag); - if (found <= 0) - break; - for (i = 0; i < found; i++) { - req = pgvec[i]; - if (req->wb_index > idx_end) - goto out; - idx_start = req->wb_index + 1; - if (nfs_lock_request_dontget(req)) { - kref_get(&req->wb_kref); - radix_tree_tag_clear(&nfsi->nfs_page_tree, - req->wb_index, tag); - list = pnfs_choose_commit_list(req, dst); - nfs_list_add_request(req, list); - res++; - if (res == INT_MAX) - goto out; - } - } - /* for latency reduction */ - cond_resched_lock(&nfsi->vfs_inode.i_lock); - } -out: - return res; -} - int __init nfs_init_nfspagecache(void) { nfs_page_cachep = kmem_cache_create("nfs_page", diff --git a/fs/nfs/pnfs.h b/fs/nfs/pnfs.h index 8088d51f495e..ef92f676cf1e 100644 --- a/fs/nfs/pnfs.h +++ b/fs/nfs/pnfs.h @@ -94,11 +94,10 @@ struct pnfs_layoutdriver_type { const struct nfs_pageio_ops *pg_read_ops; const struct nfs_pageio_ops *pg_write_ops; - /* Returns true if layoutdriver wants to divert this request to - * driver's commit routine. - */ - bool (*mark_pnfs_commit)(struct pnfs_layout_segment *lseg); - struct list_head * (*choose_commit_list) (struct nfs_page *req); + struct list_head * (*choose_commit_list) (struct nfs_page *req, + struct pnfs_layout_segment *lseg); + struct pnfs_layout_segment *(*remove_commit_req) (struct nfs_page *req); + int (*scan_commit_lists) (struct inode *inode, int max); int (*commit_pagelist)(struct inode *inode, struct list_head *mds_pages, int how); /* @@ -262,20 +261,6 @@ static inline int pnfs_enabled_sb(struct nfs_server *nfss) return nfss->pnfs_curr_ld != NULL; } -static inline void -pnfs_mark_request_commit(struct nfs_page *req, struct pnfs_layout_segment *lseg) -{ - if (lseg) { - struct pnfs_layoutdriver_type *ld; - - ld = NFS_SERVER(req->wb_page->mapping->host)->pnfs_curr_ld; - if (ld->mark_pnfs_commit && ld->mark_pnfs_commit(lseg)) { - set_bit(PG_PNFS_COMMIT, &req->wb_flags); - req->wb_commit_lseg = get_lseg(lseg); - } - } -} - static inline int pnfs_commit_list(struct inode *inode, struct list_head *mds_pages, int how) { @@ -285,26 +270,38 @@ pnfs_commit_list(struct inode *inode, struct list_head *mds_pages, int how) } static inline struct list_head * -pnfs_choose_commit_list(struct nfs_page *req, struct list_head *mds) +pnfs_choose_commit_list(struct nfs_page *req, struct pnfs_layout_segment *lseg) { + struct inode *inode = req->wb_context->dentry->d_inode; struct list_head *rv; - if (test_and_clear_bit(PG_PNFS_COMMIT, &req->wb_flags)) { - struct inode *inode = req->wb_commit_lseg->pls_layout->plh_inode; - - set_bit(NFS_INO_PNFS_COMMIT, &NFS_I(inode)->flags); - rv = NFS_SERVER(inode)->pnfs_curr_ld->choose_commit_list(req); - /* matched by ref taken when PG_PNFS_COMMIT is set */ - put_lseg(req->wb_commit_lseg); - } else - rv = mds; + if (lseg && NFS_SERVER(inode)->pnfs_curr_ld->choose_commit_list) + rv = NFS_SERVER(inode)->pnfs_curr_ld->choose_commit_list(req, lseg); + else + rv = &NFS_I(inode)->commit_list; return rv; } -static inline void pnfs_clear_request_commit(struct nfs_page *req) +static inline struct pnfs_layout_segment * +pnfs_clear_request_commit(struct nfs_page *req) { - if (test_and_clear_bit(PG_PNFS_COMMIT, &req->wb_flags)) - put_lseg(req->wb_commit_lseg); + struct inode *inode = req->wb_context->dentry->d_inode; + + if (NFS_SERVER(inode)->pnfs_curr_ld && + NFS_SERVER(inode)->pnfs_curr_ld->remove_commit_req) + return NFS_SERVER(inode)->pnfs_curr_ld->remove_commit_req(req); + else + return NULL; +} + +static inline int +pnfs_scan_commit_lists(struct inode *inode, int max) +{ + if (NFS_SERVER(inode)->pnfs_curr_ld && + NFS_SERVER(inode)->pnfs_curr_ld->scan_commit_lists) + return NFS_SERVER(inode)->pnfs_curr_ld->scan_commit_lists(inode, max); + else + return 0; } /* Should the pNFS client commit and return the layout upon a setattr */ @@ -400,11 +397,6 @@ static inline bool pnfs_pageio_init_write(struct nfs_pageio_descriptor *pgio, st return false; } -static inline void -pnfs_mark_request_commit(struct nfs_page *req, struct pnfs_layout_segment *lseg) -{ -} - static inline int pnfs_commit_list(struct inode *inode, struct list_head *mds_pages, int how) { @@ -412,13 +404,23 @@ pnfs_commit_list(struct inode *inode, struct list_head *mds_pages, int how) } static inline struct list_head * -pnfs_choose_commit_list(struct nfs_page *req, struct list_head *mds) +pnfs_choose_commit_list(struct nfs_page *req, struct pnfs_layout_segment *lseg) { - return mds; + struct inode *inode = req->wb_context->dentry->d_inode; + + return &NFS_I(inode)->commit_list; } -static inline void pnfs_clear_request_commit(struct nfs_page *req) +static inline struct pnfs_layout_segment * +pnfs_clear_request_commit(struct nfs_page *req) { + return NULL; +} + +static inline int +pnfs_scan_commit_lists(struct inode *inode, int max) +{ + return 0; } static inline int pnfs_layoutcommit_inode(struct inode *inode, bool sync) diff --git a/fs/nfs/write.c b/fs/nfs/write.c index fd8a4f07bc0c..a630ad65d64c 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -375,21 +375,14 @@ out_err: /* * Insert a write request into an inode */ -static int nfs_inode_add_request(struct inode *inode, struct nfs_page *req) +static void nfs_inode_add_request(struct inode *inode, struct nfs_page *req) { struct nfs_inode *nfsi = NFS_I(inode); - int error; - - error = radix_tree_preload(GFP_NOFS); - if (error != 0) - goto out; /* Lock the request! */ nfs_lock_request_dontget(req); spin_lock(&inode->i_lock); - error = radix_tree_insert(&nfsi->nfs_page_tree, req->wb_index, req); - BUG_ON(error); if (!nfsi->npages && nfs_have_delegation(inode, FMODE_WRITE)) inode->i_version++; set_bit(PG_MAPPED, &req->wb_flags); @@ -398,11 +391,10 @@ static int nfs_inode_add_request(struct inode *inode, struct nfs_page *req) nfsi->npages++; kref_get(&req->wb_kref); spin_unlock(&inode->i_lock); - radix_tree_preload_end(); -out: - return error; } +static struct pnfs_layout_segment *nfs_clear_request_commit(struct nfs_page *req); + /* * Remove a write request from an inode */ @@ -410,16 +402,18 @@ static void nfs_inode_remove_request(struct nfs_page *req) { struct inode *inode = req->wb_context->dentry->d_inode; struct nfs_inode *nfsi = NFS_I(inode); + struct pnfs_layout_segment *lseg; BUG_ON (!NFS_WBACK_BUSY(req)); spin_lock(&inode->i_lock); + lseg = nfs_clear_request_commit(req); set_page_private(req->wb_page, 0); ClearPagePrivate(req->wb_page); clear_bit(PG_MAPPED, &req->wb_flags); - radix_tree_delete(&nfsi->nfs_page_tree, req->wb_index); nfsi->npages--; spin_unlock(&inode->i_lock); + put_lseg(lseg); nfs_release_request(req); } @@ -438,31 +432,38 @@ nfs_mark_request_commit(struct nfs_page *req, struct pnfs_layout_segment *lseg) { struct inode *inode = req->wb_context->dentry->d_inode; struct nfs_inode *nfsi = NFS_I(inode); + struct list_head *clist; + clist = pnfs_choose_commit_list(req, lseg); spin_lock(&inode->i_lock); set_bit(PG_CLEAN, &(req)->wb_flags); - radix_tree_tag_set(&nfsi->nfs_page_tree, - req->wb_index, - NFS_PAGE_TAG_COMMIT); + nfs_list_add_request(req, clist); nfsi->ncommit++; spin_unlock(&inode->i_lock); - pnfs_mark_request_commit(req, lseg); inc_zone_page_state(req->wb_page, NR_UNSTABLE_NFS); inc_bdi_stat(req->wb_page->mapping->backing_dev_info, BDI_RECLAIMABLE); __mark_inode_dirty(inode, I_DIRTY_DATASYNC); } -static int +static void +nfs_clear_page_commit(struct page *page) +{ + dec_zone_page_state(page, NR_UNSTABLE_NFS); + dec_bdi_stat(page->mapping->backing_dev_info, BDI_RECLAIMABLE); +} + +static struct pnfs_layout_segment * nfs_clear_request_commit(struct nfs_page *req) { - struct page *page = req->wb_page; + struct pnfs_layout_segment *lseg = NULL; if (test_and_clear_bit(PG_CLEAN, &(req)->wb_flags)) { - dec_zone_page_state(page, NR_UNSTABLE_NFS); - dec_bdi_stat(page->mapping->backing_dev_info, BDI_RECLAIMABLE); - return 1; + nfs_clear_page_commit(req->wb_page); + lseg = pnfs_clear_request_commit(req); + NFS_I(req->wb_context->dentry->d_inode)->ncommit--; + list_del(&req->wb_list); } - return 0; + return lseg; } static inline @@ -494,10 +495,10 @@ nfs_mark_request_commit(struct nfs_page *req, struct pnfs_layout_segment *lseg) { } -static inline int +static inline struct pnfs_layout_segment * nfs_clear_request_commit(struct nfs_page *req) { - return 0; + return NULL; } static inline @@ -518,46 +519,67 @@ int nfs_reschedule_unstable_write(struct nfs_page *req, static int nfs_need_commit(struct nfs_inode *nfsi) { - return radix_tree_tagged(&nfsi->nfs_page_tree, NFS_PAGE_TAG_COMMIT); + return nfsi->ncommit > 0; } +/* i_lock held by caller */ +int +nfs_scan_commit_list(struct list_head *src, struct list_head *dst, int max) +{ + struct nfs_page *req, *tmp; + int ret = 0; + + list_for_each_entry_safe(req, tmp, src, wb_list) { + if (nfs_lock_request_dontget(req)) { + kref_get(&req->wb_kref); + list_move_tail(&req->wb_list, dst); + clear_bit(PG_CLEAN, &(req)->wb_flags); + ret++; + if (ret == max) + break; + } + } + return ret; +} +EXPORT_SYMBOL_GPL(nfs_scan_commit_list); + /* * nfs_scan_commit - Scan an inode for commit requests * @inode: NFS inode to scan * @dst: destination list - * @idx_start: lower bound of page->index to scan. - * @npages: idx_start + npages sets the upper bound to scan. * * Moves requests from the inode's 'commit' request list. * The requests are *not* checked to ensure that they form a contiguous set. */ static int -nfs_scan_commit(struct inode *inode, struct list_head *dst, pgoff_t idx_start, unsigned int npages) +nfs_scan_commit(struct inode *inode, struct list_head *dst) { struct nfs_inode *nfsi = NFS_I(inode); - int ret; - - if (!nfs_need_commit(nfsi)) - return 0; + int ret = 0; spin_lock(&inode->i_lock); - ret = nfs_scan_list(nfsi, dst, idx_start, npages, NFS_PAGE_TAG_COMMIT); - if (ret > 0) + if (nfsi->ncommit > 0) { + int pnfs_ret; + + ret = nfs_scan_commit_list(&nfsi->commit_list, dst, INT_MAX); + pnfs_ret = pnfs_scan_commit_lists(inode, INT_MAX - ret); + if (pnfs_ret) { + ret += pnfs_ret; + set_bit(NFS_INO_PNFS_COMMIT, &nfsi->flags); + } nfsi->ncommit -= ret; + } spin_unlock(&inode->i_lock); - - if (nfs_need_commit(NFS_I(inode))) - __mark_inode_dirty(inode, I_DIRTY_DATASYNC); - return ret; } + #else static inline int nfs_need_commit(struct nfs_inode *nfsi) { return 0; } -static inline int nfs_scan_commit(struct inode *inode, struct list_head *dst, pgoff_t idx_start, unsigned int npages) +static inline int nfs_scan_commit(struct inode *inode, struct list_head *dst) { return 0; } @@ -579,6 +601,7 @@ static struct nfs_page *nfs_try_to_update_request(struct inode *inode, unsigned int rqend; unsigned int end; int error; + struct pnfs_layout_segment *lseg = NULL; if (!PagePrivate(page)) return NULL; @@ -614,12 +637,7 @@ static struct nfs_page *nfs_try_to_update_request(struct inode *inode, spin_lock(&inode->i_lock); } - if (nfs_clear_request_commit(req) && - radix_tree_tag_clear(&NFS_I(inode)->nfs_page_tree, - req->wb_index, NFS_PAGE_TAG_COMMIT) != NULL) { - NFS_I(inode)->ncommit--; - pnfs_clear_request_commit(req); - } + lseg = nfs_clear_request_commit(req); /* Okay, the request matches. Update the region */ if (offset < req->wb_offset) { @@ -632,6 +650,7 @@ static struct nfs_page *nfs_try_to_update_request(struct inode *inode, req->wb_bytes = rqend - req->wb_offset; out_unlock: spin_unlock(&inode->i_lock); + put_lseg(lseg); return req; out_flushme: spin_unlock(&inode->i_lock); @@ -653,7 +672,6 @@ static struct nfs_page * nfs_setup_write_request(struct nfs_open_context* ctx, { struct inode *inode = page->mapping->host; struct nfs_page *req; - int error; req = nfs_try_to_update_request(inode, page, offset, bytes); if (req != NULL) @@ -661,11 +679,7 @@ static struct nfs_page * nfs_setup_write_request(struct nfs_open_context* ctx, req = nfs_create_request(ctx, inode, page, offset, bytes); if (IS_ERR(req)) goto out; - error = nfs_inode_add_request(inode, req); - if (error != 0) { - nfs_release_request(req); - req = ERR_PTR(error); - } + nfs_inode_add_request(inode, req); out: return req; } @@ -1458,7 +1472,7 @@ void nfs_commit_release_pages(struct nfs_write_data *data) while (!list_empty(&data->pages)) { req = nfs_list_entry(data->pages.next); nfs_list_remove_request(req); - nfs_clear_request_commit(req); + nfs_clear_page_commit(req->wb_page); dprintk("NFS: commit (%s/%lld %d@%lld)", req->wb_context->dentry->d_sb->s_id, @@ -1515,7 +1529,7 @@ int nfs_commit_inode(struct inode *inode, int how) res = nfs_commit_set_lock(NFS_I(inode), may_wait); if (res <= 0) goto out_mark_dirty; - res = nfs_scan_commit(inode, &head, 0, 0); + res = nfs_scan_commit(inode, &head); if (res) { int error; diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index ce8e4361ad14..0a63ab2b5a76 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -171,13 +171,9 @@ struct nfs_inode { */ __be32 cookieverf[2]; - /* - * This is the list of dirty unwritten pages. - */ - struct radix_tree_root nfs_page_tree; - unsigned long npages; unsigned long ncommit; + struct list_head commit_list; /* Open contexts for shared mmap writes */ struct list_head open_files; diff --git a/include/linux/nfs_page.h b/include/linux/nfs_page.h index 65b563f0903a..50856e9c1e5f 100644 --- a/include/linux/nfs_page.h +++ b/include/linux/nfs_page.h @@ -18,11 +18,6 @@ #include -/* - * Valid flags for the radix tree - */ -#define NFS_PAGE_TAG_COMMIT 1 - /* * Valid flags for a dirty buffer */ @@ -32,16 +27,12 @@ enum { PG_CLEAN, PG_NEED_COMMIT, PG_NEED_RESCHED, - PG_PNFS_COMMIT, PG_PARTIAL_READ_FAILED, }; struct nfs_inode; struct nfs_page { - union { - struct list_head wb_list; /* Defines state of page: */ - struct pnfs_layout_segment *wb_commit_lseg; /* Used when PG_PNFS_COMMIT set */ - }; + struct list_head wb_list; /* Defines state of page: */ struct page *wb_page; /* page to read in/write out */ struct nfs_open_context *wb_context; /* File state context info */ struct nfs_lock_context *wb_lock_context; /* lock context info */ @@ -89,8 +80,6 @@ extern struct nfs_page *nfs_create_request(struct nfs_open_context *ctx, extern void nfs_release_request(struct nfs_page *req); -extern int nfs_scan_list(struct nfs_inode *nfsi, struct list_head *dst, - pgoff_t idx_start, unsigned int npages, int tag); extern void nfs_pageio_init(struct nfs_pageio_descriptor *desc, struct inode *inode, const struct nfs_pageio_ops *pg_ops, -- cgit v1.2.3 From b83d2ff01376cf3799394693c0dd089f657bdf84 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 11 Mar 2012 11:49:17 +0000 Subject: regmap: Rejig struct declarations for stubbed API Ensure we have a forward declaration of struct regmap that isn't just the return value of regmap_init() and make the definition of the register defaults available. Reported-by: Randy Dunlap Signed-off-by: Mark Brown --- include/linux/regmap.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 33d5f1d9f882..14b8252d8ed0 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -16,11 +16,10 @@ #include #include -#ifdef CONFIG_REGMAP - struct module; struct i2c_client; struct spi_device; +struct regmap; /* An enum of all the supported cache types */ enum regcache_type { @@ -42,6 +41,8 @@ struct reg_default { unsigned int def; }; +#ifdef CONFIG_REGMAP + /** * Configuration for the register map of a device. * -- cgit v1.2.3 From 09acfea5d8de419ebe84be43b08f7b79c965215f Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sun, 11 Mar 2012 15:22:54 -0400 Subject: SUNRPC: Fix a few sparse warnings net/sunrpc/svcsock.c:412:22: warning: incorrect type in assignment (different address spaces) - svc_partial_recvfrom now takes a struct kvec, so the variable save_iovbase needs to be an ordinary (void *) Make a bunch of variables in net/sunrpc/xprtsock.c static Fix a couple of "warning: symbol 'foo' was not declared. Should it be static?" reports. Fix a couple of conflicting function declarations. Signed-off-by: Trond Myklebust --- include/linux/sunrpc/bc_xprt.h | 2 +- include/linux/sunrpc/svcauth.h | 3 +++ include/linux/sunrpc/xprtsock.h | 12 ------------ net/sunrpc/auth_gss/gss_krb5_mech.c | 2 +- net/sunrpc/auth_gss/gss_krb5_seal.c | 2 +- net/sunrpc/backchannel_rqst.c | 1 + net/sunrpc/rpc_pipe.c | 2 +- net/sunrpc/sunrpc_syms.c | 3 --- net/sunrpc/svcsock.c | 2 +- net/sunrpc/xprtsock.c | 10 +++++----- 10 files changed, 14 insertions(+), 25 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sunrpc/bc_xprt.h b/include/linux/sunrpc/bc_xprt.h index f7f3ce340c08..969c0a671dbf 100644 --- a/include/linux/sunrpc/bc_xprt.h +++ b/include/linux/sunrpc/bc_xprt.h @@ -35,7 +35,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. struct rpc_rqst *xprt_alloc_bc_request(struct rpc_xprt *xprt); void xprt_free_bc_request(struct rpc_rqst *req); int xprt_setup_backchannel(struct rpc_xprt *, unsigned int min_reqs); -void xprt_destroy_backchannel(struct rpc_xprt *, int max_reqs); +void xprt_destroy_backchannel(struct rpc_xprt *, unsigned int max_reqs); int bc_send(struct rpc_rqst *req); /* diff --git a/include/linux/sunrpc/svcauth.h b/include/linux/sunrpc/svcauth.h index 25d333c1b571..548790e9113b 100644 --- a/include/linux/sunrpc/svcauth.h +++ b/include/linux/sunrpc/svcauth.h @@ -135,6 +135,9 @@ extern void svcauth_unix_purge(void); extern void svcauth_unix_info_release(struct svc_xprt *xpt); extern int svcauth_unix_set_client(struct svc_rqst *rqstp); +extern int unix_gid_cache_create(struct net *net); +extern void unix_gid_cache_destroy(struct net *net); + static inline unsigned long hash_str(char *name, int bits) { unsigned long hash = 0; diff --git a/include/linux/sunrpc/xprtsock.h b/include/linux/sunrpc/xprtsock.h index 3f14a02e9cc0..1ad36cc25b2e 100644 --- a/include/linux/sunrpc/xprtsock.h +++ b/include/linux/sunrpc/xprtsock.h @@ -12,18 +12,6 @@ int init_socket_xprt(void); void cleanup_socket_xprt(void); -/* - * RPC slot table sizes for UDP, TCP transports - */ -extern unsigned int xprt_udp_slot_table_entries; -extern unsigned int xprt_tcp_slot_table_entries; - -/* - * Parameters for choosing a free port - */ -extern unsigned int xprt_min_resvport; -extern unsigned int xprt_max_resvport; - #define RPC_MIN_RESVPORT (1U) #define RPC_MAX_RESVPORT (65535U) #define RPC_DEF_MIN_RESVPORT (665U) diff --git a/net/sunrpc/auth_gss/gss_krb5_mech.c b/net/sunrpc/auth_gss/gss_krb5_mech.c index 8c67890de427..8eff8c32d1b9 100644 --- a/net/sunrpc/auth_gss/gss_krb5_mech.c +++ b/net/sunrpc/auth_gss/gss_krb5_mech.c @@ -344,7 +344,7 @@ out_err: return PTR_ERR(p); } -struct crypto_blkcipher * +static struct crypto_blkcipher * context_v2_alloc_cipher(struct krb5_ctx *ctx, const char *cname, u8 *key) { struct crypto_blkcipher *cp; diff --git a/net/sunrpc/auth_gss/gss_krb5_seal.c b/net/sunrpc/auth_gss/gss_krb5_seal.c index d7941eab7796..62ae3273186c 100644 --- a/net/sunrpc/auth_gss/gss_krb5_seal.c +++ b/net/sunrpc/auth_gss/gss_krb5_seal.c @@ -159,7 +159,7 @@ gss_get_mic_v1(struct krb5_ctx *ctx, struct xdr_buf *text, return (ctx->endtime < now) ? GSS_S_CONTEXT_EXPIRED : GSS_S_COMPLETE; } -u32 +static u32 gss_get_mic_v2(struct krb5_ctx *ctx, struct xdr_buf *text, struct xdr_netobj *token) { diff --git a/net/sunrpc/backchannel_rqst.c b/net/sunrpc/backchannel_rqst.c index 3ad435a14ada..31def68a0f6e 100644 --- a/net/sunrpc/backchannel_rqst.c +++ b/net/sunrpc/backchannel_rqst.c @@ -25,6 +25,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include +#include #ifdef RPC_DEBUG #define RPCDBG_FACILITY RPCDBG_TRANS diff --git a/net/sunrpc/rpc_pipe.c b/net/sunrpc/rpc_pipe.c index 7d96e3cd57cc..8584ec068e97 100644 --- a/net/sunrpc/rpc_pipe.c +++ b/net/sunrpc/rpc_pipe.c @@ -1149,7 +1149,7 @@ rpc_mount(struct file_system_type *fs_type, return mount_ns(fs_type, flags, current->nsproxy->net_ns, rpc_fill_super); } -void rpc_kill_sb(struct super_block *sb) +static void rpc_kill_sb(struct super_block *sb) { struct net *net = sb->s_fs_info; struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); diff --git a/net/sunrpc/sunrpc_syms.c b/net/sunrpc/sunrpc_syms.c index 21d106e2ca06..8adfc88e793a 100644 --- a/net/sunrpc/sunrpc_syms.c +++ b/net/sunrpc/sunrpc_syms.c @@ -27,9 +27,6 @@ int sunrpc_net_id; EXPORT_SYMBOL_GPL(sunrpc_net_id); -extern int unix_gid_cache_create(struct net *net); -extern int unix_gid_cache_destroy(struct net *net); - static __net_init int sunrpc_init_net(struct net *net) { int err; diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c index e088b1633d36..40ae884db865 100644 --- a/net/sunrpc/svcsock.c +++ b/net/sunrpc/svcsock.c @@ -396,7 +396,7 @@ static int svc_partial_recvfrom(struct svc_rqst *rqstp, int buflen, unsigned int base) { size_t save_iovlen; - void __user *save_iovbase; + void *save_iovbase; unsigned int i; int ret; diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c index 4c8281d29e2b..92bc5181dbeb 100644 --- a/net/sunrpc/xprtsock.c +++ b/net/sunrpc/xprtsock.c @@ -53,12 +53,12 @@ static void xs_close(struct rpc_xprt *xprt); /* * xprtsock tunables */ -unsigned int xprt_udp_slot_table_entries = RPC_DEF_SLOT_TABLE; -unsigned int xprt_tcp_slot_table_entries = RPC_MIN_SLOT_TABLE; -unsigned int xprt_max_tcp_slot_table_entries = RPC_MAX_SLOT_TABLE; +static unsigned int xprt_udp_slot_table_entries = RPC_DEF_SLOT_TABLE; +static unsigned int xprt_tcp_slot_table_entries = RPC_MIN_SLOT_TABLE; +static unsigned int xprt_max_tcp_slot_table_entries = RPC_MAX_SLOT_TABLE; -unsigned int xprt_min_resvport = RPC_DEF_MIN_RESVPORT; -unsigned int xprt_max_resvport = RPC_DEF_MAX_RESVPORT; +static unsigned int xprt_min_resvport = RPC_DEF_MIN_RESVPORT; +static unsigned int xprt_max_resvport = RPC_DEF_MAX_RESVPORT; #define XS_TCP_LINGER_TO (15U * HZ) static unsigned int xs_tcp_fin_timeout __read_mostly = XS_TCP_LINGER_TO; -- cgit v1.2.3 From e34828298ec542294f4b798606ee73e462d322f5 Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Wed, 29 Feb 2012 22:16:13 +0900 Subject: sh: introduce sh_clk_ops in parallel with clk_ops Introduce sh_clk_ops in parallel with clk_ops. Signed-off-by: Magnus Damm Signed-off-by: Rafael J. Wysocki --- include/linux/sh_clk.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include/linux') diff --git a/include/linux/sh_clk.h b/include/linux/sh_clk.h index 54341d811685..706c005cd4ba 100644 --- a/include/linux/sh_clk.h +++ b/include/linux/sh_clk.h @@ -18,6 +18,8 @@ struct clk_mapping { struct kref ref; }; +#define sh_clk_ops clk_ops + struct clk_ops { #ifdef CONFIG_SH_CLK_CPG_LEGACY void (*init)(struct clk *clk); -- cgit v1.2.3 From 84c36ffd7c580e1a63d7284e318670b082260118 Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Wed, 29 Feb 2012 22:18:19 +0900 Subject: sh: remove clk_ops Now when all clk_ops have been renamed it is safe to rename clk_ops to sh_clk_ops. Signed-off-by: Magnus Damm Signed-off-by: Rafael J. Wysocki --- include/linux/sh_clk.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sh_clk.h b/include/linux/sh_clk.h index 706c005cd4ba..0a9d8f2ac519 100644 --- a/include/linux/sh_clk.h +++ b/include/linux/sh_clk.h @@ -18,9 +18,8 @@ struct clk_mapping { struct kref ref; }; -#define sh_clk_ops clk_ops -struct clk_ops { +struct sh_clk_ops { #ifdef CONFIG_SH_CLK_CPG_LEGACY void (*init)(struct clk *clk); #endif @@ -39,7 +38,7 @@ struct clk { unsigned short parent_num; /* choose between */ unsigned char src_shift; /* source clock field in the */ unsigned char src_width; /* configuration register */ - struct clk_ops *ops; + struct sh_clk_ops *ops; struct list_head children; struct list_head sibling; /* node for children */ -- cgit v1.2.3 From 394349f7789fdfcdc74b61afcac84046535c40b7 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 24 Nov 2011 18:27:15 +0100 Subject: pinctrl: introduce generic pin config This is a split-off from the earlier patch set which adds generic pin configuration for the pin controllers that want it. Since we may have a system with mixed generic and custom pin controllers, we pass a boolean in the pin controller ops vtable to indicate if it is generic. ChangeLog v1->v5: - Follow parent patch versioning number system. - Document the semantic meaning of return values from pin config get functions, so we can iterate over pins and check their properties from debugfs as part of the generic config code. - Use proper cast functions in the generic debugfs pin config file. - Expand generic config to optionally cover groups too. ChangeLog v5->v6: - Update to match underlying changes. ChangeLog v6->v7: - Drop DRIVE_OFF parameter, use bias high impedance for this - Delete argument for drive modes push-pull, od and os. These are now just state transitions. - Delete slew rate rising/falling due to discussions on on proper semantics - Drop config wakeup, struct irq_chip does this for now, add back if need be. - Set PIN_CONFIG_END to 0x7fff making room for custom config parameters from 0x8000 and up. - Prefix accessor functions with pinconf_ --- drivers/pinctrl/Kconfig | 4 ++ drivers/pinctrl/Makefile | 1 + drivers/pinctrl/pinconf-generic.c | 120 ++++++++++++++++++++++++++++++++ drivers/pinctrl/pinconf.c | 6 +- drivers/pinctrl/pinconf.h | 43 +++++++++++- include/linux/pinctrl/pinconf-generic.h | 114 ++++++++++++++++++++++++++++++ include/linux/pinctrl/pinconf.h | 5 ++ 7 files changed, 289 insertions(+), 4 deletions(-) create mode 100644 drivers/pinctrl/pinconf-generic.c create mode 100644 include/linux/pinctrl/pinconf-generic.h (limited to 'include/linux') diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index c6d29ff61d7e..07f3d8d38580 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -17,6 +17,10 @@ config PINMUX config PINCONF bool "Support pin configuration controllers" +config GENERIC_PINCONF + bool + select PINCONF + config DEBUG_PINCTRL bool "Debug PINCTRL calls" depends on DEBUG_KERNEL diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index 754329b9c611..6d4150b4eced 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -5,6 +5,7 @@ ccflags-$(CONFIG_DEBUG_PINCTRL) += -DDEBUG obj-$(CONFIG_PINCTRL) += core.o obj-$(CONFIG_PINMUX) += pinmux.o obj-$(CONFIG_PINCONF) += pinconf.o +obj-$(CONFIG_GENERIC_PINCONF) += pinconf-generic.o obj-$(CONFIG_PINCTRL_PXA3xx) += pinctrl-pxa3xx.o obj-$(CONFIG_PINCTRL_MMP2) += pinctrl-mmp2.o obj-$(CONFIG_PINCTRL_PXA168) += pinctrl-pxa168.o diff --git a/drivers/pinctrl/pinconf-generic.c b/drivers/pinctrl/pinconf-generic.c new file mode 100644 index 000000000000..33fbaeaa65dd --- /dev/null +++ b/drivers/pinctrl/pinconf-generic.c @@ -0,0 +1,120 @@ +/* + * Core driver for the generic pin config portions of the pin control subsystem + * + * Copyright (C) 2011 ST-Ericsson SA + * Written on behalf of Linaro for ST-Ericsson + * + * Author: Linus Walleij + * + * License terms: GNU General Public License (GPL) version 2 + */ + +#define pr_fmt(fmt) "generic pinconfig core: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "core.h" +#include "pinconf.h" + +#ifdef CONFIG_DEBUG_FS + +struct pin_config_item { + const enum pin_config_param param; + const char * const display; + const char * const format; +}; + +#define PCONFDUMP(a, b, c) { .param = a, .display = b, .format = c } + +struct pin_config_item conf_items[] = { + PCONFDUMP(PIN_CONFIG_BIAS_DISABLE, "input bias disabled", NULL), + PCONFDUMP(PIN_CONFIG_BIAS_HIGH_IMPEDANCE, "input bias high impedance", NULL), + PCONFDUMP(PIN_CONFIG_BIAS_PULL_UP, "input bias pull up", NULL), + PCONFDUMP(PIN_CONFIG_BIAS_PULL_DOWN, "input bias pull down", NULL), + PCONFDUMP(PIN_CONFIG_DRIVE_PUSH_PULL, "output drive push pull", NULL), + PCONFDUMP(PIN_CONFIG_DRIVE_OPEN_DRAIN, "output drive open drain", NULL), + PCONFDUMP(PIN_CONFIG_DRIVE_OPEN_SOURCE, "output drive open source", NULL), + PCONFDUMP(PIN_CONFIG_INPUT_SCHMITT, "input schmitt trigger", NULL), + PCONFDUMP(PIN_CONFIG_INPUT_DEBOUNCE, "input debounce", "time units"), + PCONFDUMP(PIN_CONFIG_POWER_SOURCE, "pin power source", "selector"), + PCONFDUMP(PIN_CONFIG_LOW_POWER_MODE, "pin low power", "mode"), +}; + +void pinconf_generic_dump_pin(struct pinctrl_dev *pctldev, + struct seq_file *s, unsigned pin) +{ + const struct pinconf_ops *ops = pctldev->desc->confops; + int i; + + if (!ops->is_generic) + return; + + for(i = 0; i < ARRAY_SIZE(conf_items); i++) { + unsigned long config; + int ret; + + /* We want to check out this parameter */ + config = pinconf_to_config_packed(conf_items[i].param, 0); + ret = pin_config_get_for_pin(pctldev, pin, &config); + /* These are legal errors */ + if (ret == -EINVAL || ret == -ENOTSUPP) + continue; + if (ret) { + seq_printf(s, "ERROR READING CONFIG SETTING %d ", i); + continue; + } + /* Space between multiple configs */ + seq_puts(s, " "); + seq_puts(s, conf_items[i].display); + /* Print unit if available */ + if (conf_items[i].format && + pinconf_to_config_argument(config) != 0) + seq_printf(s, " (%u %s)", + pinconf_to_config_argument(config), + conf_items[i].format); + } +} + +void pinconf_generic_dump_group(struct pinctrl_dev *pctldev, + struct seq_file *s, const char *gname) +{ + const struct pinconf_ops *ops = pctldev->desc->confops; + int i; + + if (!ops->is_generic) + return; + + for(i = 0; i < ARRAY_SIZE(conf_items); i++) { + unsigned long config; + int ret; + + /* We want to check out this parameter */ + config = pinconf_to_config_packed(conf_items[i].param, 0); + ret = pin_config_group_get(dev_name(pctldev->dev), gname, + &config); + /* These are legal errors */ + if (ret == -EINVAL || ret == -ENOTSUPP) + continue; + if (ret) { + seq_printf(s, "ERROR READING CONFIG SETTING %d ", i); + continue; + } + /* Space between multiple configs */ + seq_puts(s, " "); + seq_puts(s, conf_items[i].display); + /* Print unit if available */ + if (conf_items[i].format && config != 0) + seq_printf(s, " (%u %s)", + pinconf_to_config_argument(config), + conf_items[i].format); + } +} + +#endif diff --git a/drivers/pinctrl/pinconf.c b/drivers/pinctrl/pinconf.c index b40ac1b4fb17..7321e8601294 100644 --- a/drivers/pinctrl/pinconf.c +++ b/drivers/pinctrl/pinconf.c @@ -54,7 +54,7 @@ int pinconf_validate_map(struct pinctrl_map const *map, int i) return 0; } -static int pin_config_get_for_pin(struct pinctrl_dev *pctldev, unsigned pin, +int pin_config_get_for_pin(struct pinctrl_dev *pctldev, unsigned pin, unsigned long *config) { const struct pinconf_ops *ops = pctldev->desc->confops; @@ -439,6 +439,8 @@ static void pinconf_dump_pin(struct pinctrl_dev *pctldev, { const struct pinconf_ops *ops = pctldev->desc->confops; + /* no-op when not using generic pin config */ + pinconf_generic_dump_pin(pctldev, s, pin); if (ops && ops->pin_config_dbg_show) ops->pin_config_dbg_show(pctldev, s, pin); } @@ -482,6 +484,8 @@ static void pinconf_dump_group(struct pinctrl_dev *pctldev, { const struct pinconf_ops *ops = pctldev->desc->confops; + /* no-op when not using generic pin config */ + pinconf_generic_dump_group(pctldev, s, gname); if (ops && ops->pin_config_group_dbg_show) ops->pin_config_group_dbg_show(pctldev, s, selector); } diff --git a/drivers/pinctrl/pinconf.h b/drivers/pinctrl/pinconf.h index 0ded227661a5..54510de5e8c6 100644 --- a/drivers/pinctrl/pinconf.h +++ b/drivers/pinctrl/pinconf.h @@ -14,20 +14,26 @@ #ifdef CONFIG_PINCONF int pinconf_check_ops(struct pinctrl_dev *pctldev); - int pinconf_validate_map(struct pinctrl_map const *map, int i); - int pinconf_map_to_setting(struct pinctrl_map const *map, struct pinctrl_setting *setting); void pinconf_free_setting(struct pinctrl_setting const *setting); int pinconf_apply_setting(struct pinctrl_setting const *setting); - void pinconf_show_map(struct seq_file *s, struct pinctrl_map const *map); void pinconf_show_setting(struct seq_file *s, struct pinctrl_setting const *setting); void pinconf_init_device_debugfs(struct dentry *devroot, struct pinctrl_dev *pctldev); +/* + * You will only be interested in these if you're using PINCONF + * so don't supply any stubs for these. + */ +int pin_config_get_for_pin(struct pinctrl_dev *pctldev, unsigned pin, + unsigned long *config); +int pin_config_group_get(const char *dev_name, const char *pin_group, + unsigned long *config); + #else static inline int pinconf_check_ops(struct pinctrl_dev *pctldev) @@ -71,3 +77,34 @@ static inline void pinconf_init_device_debugfs(struct dentry *devroot, } #endif + +/* + * The following functions are available if the driver uses the generic + * pin config. + */ + +#ifdef CONFIG_GENERIC_PINCONF + +void pinconf_generic_dump_pin(struct pinctrl_dev *pctldev, + struct seq_file *s, unsigned pin); + +void pinconf_generic_dump_group(struct pinctrl_dev *pctldev, + struct seq_file *s, const char *gname); + +#else + +static inline void pinconf_generic_dump_pin(struct pinctrl_dev *pctldev, + struct seq_file *s, + unsigned pin) +{ + return; +} + +static inline void pinconf_generic_dump_group(struct pinctrl_dev *pctldev, + struct seq_file *s, + const char *gname) +{ + return; +} + +#endif diff --git a/include/linux/pinctrl/pinconf-generic.h b/include/linux/pinctrl/pinconf-generic.h new file mode 100644 index 000000000000..4f0abb9f1c09 --- /dev/null +++ b/include/linux/pinctrl/pinconf-generic.h @@ -0,0 +1,114 @@ +/* + * Interface the generic pinconfig portions of the pinctrl subsystem + * + * Copyright (C) 2011 ST-Ericsson SA + * Written on behalf of Linaro for ST-Ericsson + * This interface is used in the core to keep track of pins. + * + * Author: Linus Walleij + * + * License terms: GNU General Public License (GPL) version 2 + */ +#ifndef __LINUX_PINCTRL_PINCONF_GENERIC_H +#define __LINUX_PINCTRL_PINCONF_GENERIC_H + +/* + * You shouldn't even be able to compile with these enums etc unless you're + * using generic pin config. That is why this is defined out. + */ +#ifdef CONFIG_GENERIC_PINCONF + +/** + * enum pin_config_param - possible pin configuration parameters + * @PIN_CONFIG_BIAS_DISABLE: disable any pin bias on the pin, a + * transition from say pull-up to pull-down implies that you disable + * pull-up in the process, this setting disables all biasing. + * @PIN_CONFIG_BIAS_HIGH_IMPEDANCE: the pin will be set to a high impedance + * mode, also know as "third-state" (tristate) or "high-Z" or "floating". + * On output pins this effectively disconnects the pin, which is useful + * if for example some other pin is going to drive the signal connected + * to it for a while. Pins used for input are usually always high + * impedance. + * @PIN_CONFIG_BIAS_PULL_UP: the pin will be pulled up (usually with high + * impedance to VDD). If the argument is != 0 pull-up is enabled, + * if it is 0, pull-up is disabled. + * @PIN_CONFIG_BIAS_PULL_DOWN: the pin will be pulled down (usually with high + * impedance to GROUND). If the argument is != 0 pull-down is enabled, + * if it is 0, pull-down is disabled. + * @PIN_CONFIG_DRIVE_PUSH_PULL: the pin will be driven actively high and + * low, this is the most typical case and is typically achieved with two + * active transistors on the output. Sending this config will enabale + * push-pull mode, the argument is ignored. + * @PIN_CONFIG_DRIVE_OPEN_DRAIN: the pin will be driven with open drain (open + * collector) which means it is usually wired with other output ports + * which are then pulled up with an external resistor. Sending this + * config will enabale open drain mode, the argument is ignored. + * @PIN_CONFIG_DRIVE_OPEN_SOURCE: the pin will be driven with open source + * (open emitter). Sending this config will enabale open drain mode, the + * argument is ignored. + * @PIN_CONFIG_INPUT_SCHMITT: this will configure an input pin to run in + * schmitt-trigger mode. If the schmitt-trigger has adjustable hysteresis, + * the threshold value is given on a custom format as argument when + * setting pins to this mode. The argument zero turns the schmitt trigger + * off. + * @PIN_CONFIG_INPUT_DEBOUNCE: this will configure the pin to debounce mode, + * which means it will wait for signals to settle when reading inputs. The + * argument gives the debounce time on a custom format. Setting the + * argument to zero turns debouncing off. + * @PIN_CONFIG_POWER_SOURCE: if the pin can select between different power + * supplies, the argument to this parameter (on a custom format) tells + * the driver which alternative power source to use. + * @PIN_CONFIG_LOW_POWER_MODE: this will configure the pin for low power + * operation, if several modes of operation are supported these can be + * passed in the argument on a custom form, else just use argument 1 + * to indicate low power mode, argument 0 turns low power mode off. + * @PIN_CONFIG_END: this is the last enumerator for pin configurations, if + * you need to pass in custom configurations to the pin controller, use + * PIN_CONFIG_END+1 as the base offset. + */ +enum pin_config_param { + PIN_CONFIG_BIAS_DISABLE, + PIN_CONFIG_BIAS_HIGH_IMPEDANCE, + PIN_CONFIG_BIAS_PULL_UP, + PIN_CONFIG_BIAS_PULL_DOWN, + PIN_CONFIG_DRIVE_PUSH_PULL, + PIN_CONFIG_DRIVE_OPEN_DRAIN, + PIN_CONFIG_DRIVE_OPEN_SOURCE, + PIN_CONFIG_INPUT_SCHMITT, + PIN_CONFIG_INPUT_DEBOUNCE, + PIN_CONFIG_POWER_SOURCE, + PIN_CONFIG_LOW_POWER_MODE, + PIN_CONFIG_END = 0x7FFF, +}; + +/* + * Helpful configuration macro to be used in tables etc. + */ +#define PIN_CONF_PACKED(p, a) ((a << 16) | ((unsigned long) p & 0xffffUL)) + +/* + * The following inlines stuffs a configuration parameter and data value + * into and out of an unsigned long argument, as used by the generic pin config + * system. We put the parameter in the lower 16 bits and the argument in the + * upper 16 bits. + */ + +static inline enum pin_config_param pinconf_to_config_param(unsigned long config) +{ + return (enum pin_config_param) (config & 0xffffUL); +} + +static inline u16 pinconf_to_config_argument(unsigned long config) +{ + return (enum pin_config_param) ((config >> 16) & 0xffffUL); +} + +static inline unsigned long pinconf_to_config_packed(enum pin_config_param param, + u16 argument) +{ + return PIN_CONF_PACKED(param, argument); +} + +#endif /* CONFIG_GENERIC_PINCONF */ + +#endif /* __LINUX_PINCTRL_PINCONF_GENERIC_H */ diff --git a/include/linux/pinctrl/pinconf.h b/include/linux/pinctrl/pinconf.h index f8cf156873d7..ec431f03362d 100644 --- a/include/linux/pinctrl/pinconf.h +++ b/include/linux/pinctrl/pinconf.h @@ -20,6 +20,8 @@ struct seq_file; /** * struct pinconf_ops - pin config operations, to be implemented by * pin configuration capable drivers. + * @is_generic: for pin controllers that want to use the generic interface, + * this flag tells the framework that it's generic. * @pin_config_get: get the config of a certain pin, if the requested config * is not available on this controller this should return -ENOTSUPP * and if it is available but disabled it should return -EINVAL @@ -33,6 +35,9 @@ struct seq_file; * per-device info for a certain group in debugfs */ struct pinconf_ops { +#ifdef CONFIG_GENERIC_PINCONF + bool is_generic; +#endif int (*pin_config_get) (struct pinctrl_dev *pctldev, unsigned pin, unsigned long *config); -- cgit v1.2.3 From 24bcc89c7e7c64982e6192b4952a0a92379fc341 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Tue, 13 Mar 2012 15:41:04 -0400 Subject: jbd2: split updating of journal superblock and marking journal empty There are three case of updating journal superblock. In the first case, we want to mark journal as empty (setting s_sequence to 0), in the second case we want to update log tail, in the third case we want to update s_errno. Split these cases into separate functions. It makes the code slightly more straightforward and later patches will make the distinction even more important. Signed-off-by: Jan Kara Signed-off-by: "Theodore Ts'o" --- fs/jbd2/checkpoint.c | 2 +- fs/jbd2/commit.c | 2 +- fs/jbd2/journal.c | 163 ++++++++++++++++++++++++++------------------ include/linux/jbd2.h | 2 +- include/trace/events/jbd2.h | 12 ++-- 5 files changed, 102 insertions(+), 79 deletions(-) (limited to 'include/linux') diff --git a/fs/jbd2/checkpoint.c b/fs/jbd2/checkpoint.c index 253e91890e71..19dcd0b86bca 100644 --- a/fs/jbd2/checkpoint.c +++ b/fs/jbd2/checkpoint.c @@ -550,7 +550,7 @@ int jbd2_cleanup_journal_tail(journal_t *journal) (journal->j_flags & JBD2_BARRIER)) blkdev_issue_flush(journal->j_fs_dev, GFP_KERNEL, NULL); if (!(journal->j_flags & JBD2_ABORT)) - jbd2_journal_update_superblock(journal, 1); + jbd2_journal_update_sb_log_tail(journal); return 0; } diff --git a/fs/jbd2/commit.c b/fs/jbd2/commit.c index 8adc5d460f56..19371a8a9015 100644 --- a/fs/jbd2/commit.c +++ b/fs/jbd2/commit.c @@ -340,7 +340,7 @@ void jbd2_journal_commit_transaction(journal_t *journal) /* Do we need to erase the effects of a prior jbd2_journal_flush? */ if (journal->j_flags & JBD2_FLUSHED) { jbd_debug(3, "super block updated\n"); - jbd2_journal_update_superblock(journal, 1); + jbd2_journal_update_sb_log_tail(journal); } else { jbd_debug(3, "superblock not updated\n"); } diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c index cfb36d99f7a4..6e75fbd75bad 100644 --- a/fs/jbd2/journal.c +++ b/fs/jbd2/journal.c @@ -1110,40 +1110,30 @@ static int journal_reset(journal_t *journal) journal->j_max_transaction_buffers = journal->j_maxlen / 4; - /* Add the dynamic fields and write it to disk. */ - jbd2_journal_update_superblock(journal, 1); - return jbd2_journal_start_thread(journal); -} - -/** - * void jbd2_journal_update_superblock() - Update journal sb on disk. - * @journal: The journal to update. - * @wait: Set to '0' if you don't want to wait for IO completion. - * - * Update a journal's dynamic superblock fields and write it to disk, - * optionally waiting for the IO to complete. - */ -void jbd2_journal_update_superblock(journal_t *journal, int wait) -{ - journal_superblock_t *sb = journal->j_superblock; - struct buffer_head *bh = journal->j_sb_buffer; - /* * As a special case, if the on-disk copy is already marked as needing - * no recovery (s_start == 0) and there are no outstanding transactions - * in the filesystem, then we can safely defer the superblock update - * until the next commit by setting JBD2_FLUSHED. This avoids + * no recovery (s_start == 0), then we can safely defer the superblock + * update until the next commit by setting JBD2_FLUSHED. This avoids * attempting a write to a potential-readonly device. */ - if (sb->s_start == 0 && journal->j_tail_sequence == - journal->j_transaction_sequence) { + if (sb->s_start == 0) { jbd_debug(1, "JBD2: Skipping superblock update on recovered sb " "(start %ld, seq %d, errno %d)\n", journal->j_tail, journal->j_tail_sequence, journal->j_errno); - goto out; + journal->j_flags |= JBD2_FLUSHED; + } else { + /* Add the dynamic fields and write it to disk. */ + jbd2_journal_update_sb_log_tail(journal); } + return jbd2_journal_start_thread(journal); +} +static void jbd2_write_superblock(journal_t *journal) +{ + struct buffer_head *bh = journal->j_sb_buffer; + + trace_jbd2_write_superblock(journal); if (buffer_write_io_error(bh)) { /* * Oh, dear. A previous attempt to write the journal @@ -1160,49 +1150,98 @@ void jbd2_journal_update_superblock(journal_t *journal, int wait) set_buffer_uptodate(bh); } + BUFFER_TRACE(bh, "marking dirty"); + mark_buffer_dirty(bh); + sync_dirty_buffer(bh); + if (buffer_write_io_error(bh)) { + printk(KERN_ERR "JBD2: I/O error detected " + "when updating journal superblock for %s.\n", + journal->j_devname); + clear_buffer_write_io_error(bh); + set_buffer_uptodate(bh); + } +} + +/** + * jbd2_journal_update_sb_log_tail() - Update log tail in journal sb on disk. + * @journal: The journal to update. + * + * Update a journal's superblock information about log tail and write it to + * disk, waiting for the IO to complete. + */ +void jbd2_journal_update_sb_log_tail(journal_t *journal) +{ + journal_superblock_t *sb = journal->j_superblock; + read_lock(&journal->j_state_lock); - jbd_debug(1, "JBD2: updating superblock (start %ld, seq %d, errno %d)\n", - journal->j_tail, journal->j_tail_sequence, journal->j_errno); + jbd_debug(1, "JBD2: updating superblock (start %ld, seq %d)\n", + journal->j_tail, journal->j_tail_sequence); sb->s_sequence = cpu_to_be32(journal->j_tail_sequence); sb->s_start = cpu_to_be32(journal->j_tail); - sb->s_errno = cpu_to_be32(journal->j_errno); read_unlock(&journal->j_state_lock); - BUFFER_TRACE(bh, "marking dirty"); - mark_buffer_dirty(bh); - if (wait) { - sync_dirty_buffer(bh); - if (buffer_write_io_error(bh)) { - printk(KERN_ERR "JBD2: I/O error detected " - "when updating journal superblock for %s.\n", - journal->j_devname); - clear_buffer_write_io_error(bh); - set_buffer_uptodate(bh); - } - } else - write_dirty_buffer(bh, WRITE); + jbd2_write_superblock(journal); - trace_jbd2_update_superblock_end(journal, wait); + /* Log is no longer empty */ + write_lock(&journal->j_state_lock); + WARN_ON(!sb->s_sequence); + journal->j_flags &= ~JBD2_FLUSHED; + write_unlock(&journal->j_state_lock); +} -out: - /* If we have just flushed the log (by marking s_start==0), then - * any future commit will have to be careful to update the - * superblock again to re-record the true start of the log. */ +/** + * jbd2_mark_journal_empty() - Mark on disk journal as empty. + * @journal: The journal to update. + * + * Update a journal's dynamic superblock fields to show that journal is empty. + * Write updated superblock to disk waiting for IO to complete. + */ +static void jbd2_mark_journal_empty(journal_t *journal) +{ + journal_superblock_t *sb = journal->j_superblock; + + read_lock(&journal->j_state_lock); + jbd_debug(1, "JBD2: Marking journal as empty (seq %d)\n", + journal->j_tail_sequence); + + sb->s_sequence = cpu_to_be32(journal->j_tail_sequence); + sb->s_start = cpu_to_be32(0); + read_unlock(&journal->j_state_lock); + + jbd2_write_superblock(journal); + /* Log is no longer empty */ write_lock(&journal->j_state_lock); - if (sb->s_start) - journal->j_flags &= ~JBD2_FLUSHED; - else - journal->j_flags |= JBD2_FLUSHED; + journal->j_flags |= JBD2_FLUSHED; write_unlock(&journal->j_state_lock); } + +/** + * jbd2_journal_update_sb_errno() - Update error in the journal. + * @journal: The journal to update. + * + * Update a journal's errno. Write updated superblock to disk waiting for IO + * to complete. + */ +static void jbd2_journal_update_sb_errno(journal_t *journal) +{ + journal_superblock_t *sb = journal->j_superblock; + + read_lock(&journal->j_state_lock); + jbd_debug(1, "JBD2: updating superblock error (errno %d)\n", + journal->j_errno); + sb->s_errno = cpu_to_be32(journal->j_errno); + read_unlock(&journal->j_state_lock); + + jbd2_write_superblock(journal); +} + /* * Read the superblock for a given journal, performing initial * validation of the format. */ - static int journal_get_superblock(journal_t *journal) { struct buffer_head *bh; @@ -1395,15 +1434,10 @@ int jbd2_journal_destroy(journal_t *journal) spin_unlock(&journal->j_list_lock); if (journal->j_sb_buffer) { - if (!is_journal_aborted(journal)) { - /* We can now mark the journal as empty. */ - journal->j_tail = 0; - journal->j_tail_sequence = - ++journal->j_transaction_sequence; - jbd2_journal_update_superblock(journal, 1); - } else { + if (!is_journal_aborted(journal)) + jbd2_mark_journal_empty(journal); + else err = -EIO; - } brelse(journal->j_sb_buffer); } @@ -1562,7 +1596,6 @@ int jbd2_journal_flush(journal_t *journal) { int err = 0; transaction_t *transaction = NULL; - unsigned long old_tail; write_lock(&journal->j_state_lock); @@ -1604,14 +1637,8 @@ int jbd2_journal_flush(journal_t *journal) * the magic code for a fully-recovered superblock. Any future * commits of data to the journal will restore the current * s_start value. */ + jbd2_mark_journal_empty(journal); write_lock(&journal->j_state_lock); - old_tail = journal->j_tail; - journal->j_tail = 0; - write_unlock(&journal->j_state_lock); - jbd2_journal_update_superblock(journal, 1); - write_lock(&journal->j_state_lock); - journal->j_tail = old_tail; - J_ASSERT(!journal->j_running_transaction); J_ASSERT(!journal->j_committing_transaction); J_ASSERT(!journal->j_checkpoint_transactions); @@ -1652,7 +1679,7 @@ int jbd2_journal_wipe(journal_t *journal, int write) err = jbd2_journal_skip_recovery(journal); if (write) - jbd2_journal_update_superblock(journal, 1); + jbd2_mark_journal_empty(journal); no_recovery: return err; @@ -1702,7 +1729,7 @@ static void __journal_abort_soft (journal_t *journal, int errno) __jbd2_journal_abort_hard(journal); if (errno) - jbd2_journal_update_superblock(journal, 1); + jbd2_journal_update_sb_errno(journal); } /** diff --git a/include/linux/jbd2.h b/include/linux/jbd2.h index 46eef77e6ab8..5f05c77438e5 100644 --- a/include/linux/jbd2.h +++ b/include/linux/jbd2.h @@ -1087,7 +1087,7 @@ extern int jbd2_journal_destroy (journal_t *); extern int jbd2_journal_recover (journal_t *journal); extern int jbd2_journal_wipe (journal_t *, int); extern int jbd2_journal_skip_recovery (journal_t *); -extern void jbd2_journal_update_superblock (journal_t *, int); +extern void jbd2_journal_update_sb_log_tail (journal_t *); extern void __jbd2_journal_abort_hard (journal_t *); extern void jbd2_journal_abort (journal_t *, int); extern int jbd2_journal_errno (journal_t *); diff --git a/include/trace/events/jbd2.h b/include/trace/events/jbd2.h index ae59bc207d70..e05a362bf3f1 100644 --- a/include/trace/events/jbd2.h +++ b/include/trace/events/jbd2.h @@ -236,25 +236,21 @@ TRACE_EVENT(jbd2_cleanup_journal_tail, __entry->block_nr, __entry->freed) ); -TRACE_EVENT(jbd2_update_superblock_end, +TRACE_EVENT(jbd2_write_superblock, - TP_PROTO(journal_t *journal, int wait), + TP_PROTO(journal_t *journal), - TP_ARGS(journal, wait), + TP_ARGS(journal), TP_STRUCT__entry( __field( dev_t, dev ) - __field( int, wait ) ), TP_fast_assign( __entry->dev = journal->j_fs_dev->bd_dev; - __entry->wait = wait; ), - TP_printk("dev %d,%d wait %d", - MAJOR(__entry->dev), MINOR(__entry->dev), - __entry->wait) + TP_printk("dev %d,%d", MAJOR(__entry->dev), MINOR(__entry->dev)) ); #endif /* _TRACE_JBD2_H */ -- cgit v1.2.3 From 79feb521a44705262d15cc819a4117a447b11ea7 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Tue, 13 Mar 2012 22:22:54 -0400 Subject: jbd2: issue cache flush after checkpointing even with internal journal When we reach jbd2_cleanup_journal_tail(), there is no guarantee that checkpointed buffers are on a stable storage - especially if buffers were written out by jbd2_log_do_checkpoint(), they are likely to be only in disk's caches. Thus when we update journal superblock effectively removing old transaction from journal, this write of superblock can get to stable storage before those checkpointed buffers which can result in filesystem corruption after a crash. Thus we must unconditionally issue a cache flush before we update journal superblock in these cases. A similar problem can also occur if journal superblock is written only in disk's caches, other transaction starts reusing space of the transaction cleaned from the log and power failure happens. Subsequent journal replay would still try to replay the old transaction but some of it's blocks may be already overwritten by the new transaction. For this reason we must use WRITE_FUA when updating log tail and we must first write new log tail to disk and update in-memory information only after that. Signed-off-by: Jan Kara Signed-off-by: "Theodore Ts'o" --- fs/jbd2/checkpoint.c | 75 ++++-------------------- fs/jbd2/commit.c | 11 +++- fs/jbd2/journal.c | 138 +++++++++++++++++++++++++++++++++++++------- fs/jbd2/recovery.c | 5 +- include/linux/jbd2.h | 6 +- include/trace/events/jbd2.h | 11 ++-- 6 files changed, 155 insertions(+), 91 deletions(-) (limited to 'include/linux') diff --git a/fs/jbd2/checkpoint.c b/fs/jbd2/checkpoint.c index 19dcd0b86bca..7f7ee5b90402 100644 --- a/fs/jbd2/checkpoint.c +++ b/fs/jbd2/checkpoint.c @@ -478,79 +478,28 @@ out: int jbd2_cleanup_journal_tail(journal_t *journal) { - transaction_t * transaction; tid_t first_tid; - unsigned long blocknr, freed; + unsigned long blocknr; if (is_journal_aborted(journal)) return 1; - /* OK, work out the oldest transaction remaining in the log, and - * the log block it starts at. - * - * If the log is now empty, we need to work out which is the - * next transaction ID we will write, and where it will - * start. */ - - write_lock(&journal->j_state_lock); - spin_lock(&journal->j_list_lock); - transaction = journal->j_checkpoint_transactions; - if (transaction) { - first_tid = transaction->t_tid; - blocknr = transaction->t_log_start; - } else if ((transaction = journal->j_committing_transaction) != NULL) { - first_tid = transaction->t_tid; - blocknr = transaction->t_log_start; - } else if ((transaction = journal->j_running_transaction) != NULL) { - first_tid = transaction->t_tid; - blocknr = journal->j_head; - } else { - first_tid = journal->j_transaction_sequence; - blocknr = journal->j_head; - } - spin_unlock(&journal->j_list_lock); - J_ASSERT(blocknr != 0); - - /* If the oldest pinned transaction is at the tail of the log - already then there's not much we can do right now. */ - if (journal->j_tail_sequence == first_tid) { - write_unlock(&journal->j_state_lock); + if (!jbd2_journal_get_log_tail(journal, &first_tid, &blocknr)) return 1; - } - - /* OK, update the superblock to recover the freed space. - * Physical blocks come first: have we wrapped beyond the end of - * the log? */ - freed = blocknr - journal->j_tail; - if (blocknr < journal->j_tail) - freed = freed + journal->j_last - journal->j_first; - - trace_jbd2_cleanup_journal_tail(journal, first_tid, blocknr, freed); - jbd_debug(1, - "Cleaning journal tail from %d to %d (offset %lu), " - "freeing %lu\n", - journal->j_tail_sequence, first_tid, blocknr, freed); - - journal->j_free += freed; - journal->j_tail_sequence = first_tid; - journal->j_tail = blocknr; - write_unlock(&journal->j_state_lock); + J_ASSERT(blocknr != 0); /* - * If there is an external journal, we need to make sure that - * any data blocks that were recently written out --- perhaps - * by jbd2_log_do_checkpoint() --- are flushed out before we - * drop the transactions from the external journal. It's - * unlikely this will be necessary, especially with a - * appropriately sized journal, but we need this to guarantee - * correctness. Fortunately jbd2_cleanup_journal_tail() - * doesn't get called all that often. + * We need to make sure that any blocks that were recently written out + * --- perhaps by jbd2_log_do_checkpoint() --- are flushed out before + * we drop the transactions from the journal. It's unlikely this will + * be necessary, especially with an appropriately sized journal, but we + * need this to guarantee correctness. Fortunately + * jbd2_cleanup_journal_tail() doesn't get called all that often. */ - if ((journal->j_fs_dev != journal->j_dev) && - (journal->j_flags & JBD2_BARRIER)) + if (journal->j_flags & JBD2_BARRIER) blkdev_issue_flush(journal->j_fs_dev, GFP_KERNEL, NULL); - if (!(journal->j_flags & JBD2_ABORT)) - jbd2_journal_update_sb_log_tail(journal); + + __jbd2_update_log_tail(journal, first_tid, blocknr); return 0; } diff --git a/fs/jbd2/commit.c b/fs/jbd2/commit.c index 6705717d9b7f..b89ef84786a7 100644 --- a/fs/jbd2/commit.c +++ b/fs/jbd2/commit.c @@ -341,7 +341,16 @@ void jbd2_journal_commit_transaction(journal_t *journal) if (journal->j_flags & JBD2_FLUSHED) { jbd_debug(3, "super block updated\n"); mutex_lock(&journal->j_checkpoint_mutex); - jbd2_journal_update_sb_log_tail(journal); + /* + * We hold j_checkpoint_mutex so tail cannot change under us. + * We don't need any special data guarantees for writing sb + * since journal is empty and it is ok for write to be + * flushed only with transaction commit. + */ + jbd2_journal_update_sb_log_tail(journal, + journal->j_tail_sequence, + journal->j_tail, + WRITE_SYNC); mutex_unlock(&journal->j_checkpoint_mutex); } else { jbd_debug(3, "superblock not updated\n"); diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c index fc5f2acc9f18..c5ff177400ff 100644 --- a/fs/jbd2/journal.c +++ b/fs/jbd2/journal.c @@ -742,6 +742,85 @@ struct journal_head *jbd2_journal_get_descriptor_buffer(journal_t *journal) return jbd2_journal_add_journal_head(bh); } +/* + * Return tid of the oldest transaction in the journal and block in the journal + * where the transaction starts. + * + * If the journal is now empty, return which will be the next transaction ID + * we will write and where will that transaction start. + * + * The return value is 0 if journal tail cannot be pushed any further, 1 if + * it can. + */ +int jbd2_journal_get_log_tail(journal_t *journal, tid_t *tid, + unsigned long *block) +{ + transaction_t *transaction; + int ret; + + read_lock(&journal->j_state_lock); + spin_lock(&journal->j_list_lock); + transaction = journal->j_checkpoint_transactions; + if (transaction) { + *tid = transaction->t_tid; + *block = transaction->t_log_start; + } else if ((transaction = journal->j_committing_transaction) != NULL) { + *tid = transaction->t_tid; + *block = transaction->t_log_start; + } else if ((transaction = journal->j_running_transaction) != NULL) { + *tid = transaction->t_tid; + *block = journal->j_head; + } else { + *tid = journal->j_transaction_sequence; + *block = journal->j_head; + } + ret = tid_gt(*tid, journal->j_tail_sequence); + spin_unlock(&journal->j_list_lock); + read_unlock(&journal->j_state_lock); + + return ret; +} + +/* + * Update information in journal structure and in on disk journal superblock + * about log tail. This function does not check whether information passed in + * really pushes log tail further. It's responsibility of the caller to make + * sure provided log tail information is valid (e.g. by holding + * j_checkpoint_mutex all the time between computing log tail and calling this + * function as is the case with jbd2_cleanup_journal_tail()). + * + * Requires j_checkpoint_mutex + */ +void __jbd2_update_log_tail(journal_t *journal, tid_t tid, unsigned long block) +{ + unsigned long freed; + + BUG_ON(!mutex_is_locked(&journal->j_checkpoint_mutex)); + + /* + * We cannot afford for write to remain in drive's caches since as + * soon as we update j_tail, next transaction can start reusing journal + * space and if we lose sb update during power failure we'd replay + * old transaction with possibly newly overwritten data. + */ + jbd2_journal_update_sb_log_tail(journal, tid, block, WRITE_FUA); + write_lock(&journal->j_state_lock); + freed = block - journal->j_tail; + if (block < journal->j_tail) + freed += journal->j_last - journal->j_first; + + trace_jbd2_update_log_tail(journal, tid, block, freed); + jbd_debug(1, + "Cleaning journal tail from %d to %d (offset %lu), " + "freeing %lu\n", + journal->j_tail_sequence, tid, block, freed); + + journal->j_free += freed; + journal->j_tail_sequence = tid; + journal->j_tail = block; + write_unlock(&journal->j_state_lock); +} + struct jbd2_stats_proc_session { journal_t *journal; struct transaction_stats_s *stats; @@ -1125,18 +1204,30 @@ static int journal_reset(journal_t *journal) } else { /* Lock here to make assertions happy... */ mutex_lock(&journal->j_checkpoint_mutex); - /* Add the dynamic fields and write it to disk. */ - jbd2_journal_update_sb_log_tail(journal); + /* + * Update log tail information. We use WRITE_FUA since new + * transaction will start reusing journal space and so we + * must make sure information about current log tail is on + * disk before that. + */ + jbd2_journal_update_sb_log_tail(journal, + journal->j_tail_sequence, + journal->j_tail, + WRITE_FUA); mutex_unlock(&journal->j_checkpoint_mutex); } return jbd2_journal_start_thread(journal); } -static void jbd2_write_superblock(journal_t *journal) +static void jbd2_write_superblock(journal_t *journal, int write_op) { struct buffer_head *bh = journal->j_sb_buffer; + int ret; - trace_jbd2_write_superblock(journal); + trace_jbd2_write_superblock(journal, write_op); + if (!(journal->j_flags & JBD2_BARRIER)) + write_op &= ~(REQ_FUA | REQ_FLUSH); + lock_buffer(bh); if (buffer_write_io_error(bh)) { /* * Oh, dear. A previous attempt to write the journal @@ -1152,40 +1243,45 @@ static void jbd2_write_superblock(journal_t *journal) clear_buffer_write_io_error(bh); set_buffer_uptodate(bh); } - - BUFFER_TRACE(bh, "marking dirty"); - mark_buffer_dirty(bh); - sync_dirty_buffer(bh); + get_bh(bh); + bh->b_end_io = end_buffer_write_sync; + ret = submit_bh(write_op, bh); + wait_on_buffer(bh); if (buffer_write_io_error(bh)) { - printk(KERN_ERR "JBD2: I/O error detected " - "when updating journal superblock for %s.\n", - journal->j_devname); clear_buffer_write_io_error(bh); set_buffer_uptodate(bh); + ret = -EIO; + } + if (ret) { + printk(KERN_ERR "JBD2: Error %d detected when updating " + "journal superblock for %s.\n", ret, + journal->j_devname); } } /** * jbd2_journal_update_sb_log_tail() - Update log tail in journal sb on disk. * @journal: The journal to update. + * @tail_tid: TID of the new transaction at the tail of the log + * @tail_block: The first block of the transaction at the tail of the log + * @write_op: With which operation should we write the journal sb * * Update a journal's superblock information about log tail and write it to * disk, waiting for the IO to complete. */ -void jbd2_journal_update_sb_log_tail(journal_t *journal) +void jbd2_journal_update_sb_log_tail(journal_t *journal, tid_t tail_tid, + unsigned long tail_block, int write_op) { journal_superblock_t *sb = journal->j_superblock; BUG_ON(!mutex_is_locked(&journal->j_checkpoint_mutex)); - read_lock(&journal->j_state_lock); - jbd_debug(1, "JBD2: updating superblock (start %ld, seq %d)\n", - journal->j_tail, journal->j_tail_sequence); + jbd_debug(1, "JBD2: updating superblock (start %lu, seq %u)\n", + tail_block, tail_tid); - sb->s_sequence = cpu_to_be32(journal->j_tail_sequence); - sb->s_start = cpu_to_be32(journal->j_tail); - read_unlock(&journal->j_state_lock); + sb->s_sequence = cpu_to_be32(tail_tid); + sb->s_start = cpu_to_be32(tail_block); - jbd2_write_superblock(journal); + jbd2_write_superblock(journal, write_op); /* Log is no longer empty */ write_lock(&journal->j_state_lock); @@ -1214,7 +1310,7 @@ static void jbd2_mark_journal_empty(journal_t *journal) sb->s_start = cpu_to_be32(0); read_unlock(&journal->j_state_lock); - jbd2_write_superblock(journal); + jbd2_write_superblock(journal, WRITE_FUA); /* Log is no longer empty */ write_lock(&journal->j_state_lock); @@ -1240,7 +1336,7 @@ static void jbd2_journal_update_sb_errno(journal_t *journal) sb->s_errno = cpu_to_be32(journal->j_errno); read_unlock(&journal->j_state_lock); - jbd2_write_superblock(journal); + jbd2_write_superblock(journal, WRITE_SYNC); } /* diff --git a/fs/jbd2/recovery.c b/fs/jbd2/recovery.c index da6d7baf1390..c1a03354a22f 100644 --- a/fs/jbd2/recovery.c +++ b/fs/jbd2/recovery.c @@ -21,6 +21,7 @@ #include #include #include +#include #endif /* @@ -265,7 +266,9 @@ int jbd2_journal_recover(journal_t *journal) err2 = sync_blockdev(journal->j_fs_dev); if (!err) err = err2; - + /* Make sure all replayed data is on permanent storage */ + if (journal->j_flags & JBD2_BARRIER) + blkdev_issue_flush(journal->j_fs_dev, GFP_KERNEL, NULL); return err; } diff --git a/include/linux/jbd2.h b/include/linux/jbd2.h index 5f05c77438e5..876a7d87192b 100644 --- a/include/linux/jbd2.h +++ b/include/linux/jbd2.h @@ -971,6 +971,9 @@ extern void __journal_clean_data_list(transaction_t *transaction); /* Log buffer allocation */ extern struct journal_head * jbd2_journal_get_descriptor_buffer(journal_t *); int jbd2_journal_next_log_block(journal_t *, unsigned long long *); +int jbd2_journal_get_log_tail(journal_t *journal, tid_t *tid, + unsigned long *block); +void __jbd2_update_log_tail(journal_t *journal, tid_t tid, unsigned long block); /* Commit management */ extern void jbd2_journal_commit_transaction(journal_t *); @@ -1087,7 +1090,8 @@ extern int jbd2_journal_destroy (journal_t *); extern int jbd2_journal_recover (journal_t *journal); extern int jbd2_journal_wipe (journal_t *, int); extern int jbd2_journal_skip_recovery (journal_t *); -extern void jbd2_journal_update_sb_log_tail (journal_t *); +extern void jbd2_journal_update_sb_log_tail (journal_t *, tid_t, + unsigned long, int); extern void __jbd2_journal_abort_hard (journal_t *); extern void jbd2_journal_abort (journal_t *, int); extern int jbd2_journal_errno (journal_t *); diff --git a/include/trace/events/jbd2.h b/include/trace/events/jbd2.h index e05a362bf3f1..127993dbf322 100644 --- a/include/trace/events/jbd2.h +++ b/include/trace/events/jbd2.h @@ -207,7 +207,7 @@ TRACE_EVENT(jbd2_checkpoint_stats, __entry->forced_to_close, __entry->written, __entry->dropped) ); -TRACE_EVENT(jbd2_cleanup_journal_tail, +TRACE_EVENT(jbd2_update_log_tail, TP_PROTO(journal_t *journal, tid_t first_tid, unsigned long block_nr, unsigned long freed), @@ -238,19 +238,22 @@ TRACE_EVENT(jbd2_cleanup_journal_tail, TRACE_EVENT(jbd2_write_superblock, - TP_PROTO(journal_t *journal), + TP_PROTO(journal_t *journal, int write_op), - TP_ARGS(journal), + TP_ARGS(journal, write_op), TP_STRUCT__entry( __field( dev_t, dev ) + __field( int, write_op ) ), TP_fast_assign( __entry->dev = journal->j_fs_dev->bd_dev; + __entry->write_op = write_op; ), - TP_printk("dev %d,%d", MAJOR(__entry->dev), MINOR(__entry->dev)) + TP_printk("dev %d,%d write_op %x", MAJOR(__entry->dev), + MINOR(__entry->dev), __entry->write_op) ); #endif /* _TRACE_JBD2_H */ -- cgit v1.2.3 From 932bb305ba2a01cd62809644d569f004e77a4355 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Tue, 13 Mar 2012 22:45:25 -0400 Subject: jbd2: remove bh_state lock from checkpointing code All accesses to checkpointing entries in journal_head are protected by j_list_lock. Thus __jbd2_journal_remove_checkpoint() doesn't really need bh_state lock. Also the only part of journal head that the rest of checkpointing code needs to check is jh->b_transaction which is safe to read under j_list_lock. So we can safely remove bh_state lock from all of checkpointing code which makes it considerably prettier. Signed-off-by: Jan Kara Signed-off-by: "Theodore Ts'o" --- fs/jbd2/checkpoint.c | 59 ++++++-------------------------------------- include/linux/journal-head.h | 2 ++ 2 files changed, 9 insertions(+), 52 deletions(-) (limited to 'include/linux') diff --git a/fs/jbd2/checkpoint.c b/fs/jbd2/checkpoint.c index 546c3b300eef..c78841ee81cf 100644 --- a/fs/jbd2/checkpoint.c +++ b/fs/jbd2/checkpoint.c @@ -88,14 +88,13 @@ static inline void __buffer_relink_io(struct journal_head *jh) * whole transaction. * * Requires j_list_lock - * Called under jbd_lock_bh_state(jh2bh(jh)), and drops it */ static int __try_to_free_cp_buf(struct journal_head *jh) { int ret = 0; struct buffer_head *bh = jh2bh(jh); - if (jh->b_jlist == BJ_None && !buffer_locked(bh) && + if (jh->b_transaction == NULL && !buffer_locked(bh) && !buffer_dirty(bh) && !buffer_write_io_error(bh)) { /* * Get our reference so that bh cannot be freed before @@ -104,11 +103,8 @@ static int __try_to_free_cp_buf(struct journal_head *jh) get_bh(bh); JBUFFER_TRACE(jh, "remove from checkpoint list"); ret = __jbd2_journal_remove_checkpoint(jh) + 1; - jbd_unlock_bh_state(bh); BUFFER_TRACE(bh, "release"); __brelse(bh); - } else { - jbd_unlock_bh_state(bh); } return ret; } @@ -179,21 +175,6 @@ void __jbd2_log_wait_for_space(journal_t *journal) } } -/* - * We were unable to perform jbd_trylock_bh_state() inside j_list_lock. - * The caller must restart a list walk. Wait for someone else to run - * jbd_unlock_bh_state(). - */ -static void jbd_sync_bh(journal_t *journal, struct buffer_head *bh) - __releases(journal->j_list_lock) -{ - get_bh(bh); - spin_unlock(&journal->j_list_lock); - jbd_lock_bh_state(bh); - jbd_unlock_bh_state(bh); - put_bh(bh); -} - /* * Clean up transaction's list of buffers submitted for io. * We wait for any pending IO to complete and remove any clean @@ -222,15 +203,9 @@ restart: while (!released && transaction->t_checkpoint_io_list) { jh = transaction->t_checkpoint_io_list; bh = jh2bh(jh); - if (!jbd_trylock_bh_state(bh)) { - jbd_sync_bh(journal, bh); - spin_lock(&journal->j_list_lock); - goto restart; - } get_bh(bh); if (buffer_locked(bh)) { spin_unlock(&journal->j_list_lock); - jbd_unlock_bh_state(bh); wait_on_buffer(bh); /* the journal_head may have gone by now */ BUFFER_TRACE(bh, "brelse"); @@ -246,7 +221,6 @@ restart: * it has been written out and so we can drop it from the list */ released = __jbd2_journal_remove_checkpoint(jh); - jbd_unlock_bh_state(bh); __brelse(bh); } @@ -280,7 +254,6 @@ __flush_batch(journal_t *journal, int *batch_count) * be written out. * * Called with j_list_lock held and drops it if 1 is returned - * Called under jbd_lock_bh_state(jh2bh(jh)), and drops it */ static int __process_buffer(journal_t *journal, struct journal_head *jh, int *batch_count, transaction_t *transaction) @@ -291,7 +264,6 @@ static int __process_buffer(journal_t *journal, struct journal_head *jh, if (buffer_locked(bh)) { get_bh(bh); spin_unlock(&journal->j_list_lock); - jbd_unlock_bh_state(bh); wait_on_buffer(bh); /* the journal_head may have gone by now */ BUFFER_TRACE(bh, "brelse"); @@ -303,7 +275,6 @@ static int __process_buffer(journal_t *journal, struct journal_head *jh, transaction->t_chp_stats.cs_forced_to_close++; spin_unlock(&journal->j_list_lock); - jbd_unlock_bh_state(bh); if (unlikely(journal->j_flags & JBD2_UNMOUNT)) /* * The journal thread is dead; so starting and @@ -322,11 +293,9 @@ static int __process_buffer(journal_t *journal, struct journal_head *jh, if (unlikely(buffer_write_io_error(bh))) ret = -EIO; get_bh(bh); - J_ASSERT_JH(jh, !buffer_jbddirty(bh)); BUFFER_TRACE(bh, "remove from checkpoint"); __jbd2_journal_remove_checkpoint(jh); spin_unlock(&journal->j_list_lock); - jbd_unlock_bh_state(bh); __brelse(bh); } else { /* @@ -341,7 +310,6 @@ static int __process_buffer(journal_t *journal, struct journal_head *jh, J_ASSERT_BH(bh, !buffer_jwrite(bh)); journal->j_chkpt_bhs[*batch_count] = bh; __buffer_relink_io(jh); - jbd_unlock_bh_state(bh); transaction->t_chp_stats.cs_written++; (*batch_count)++; if (*batch_count == JBD2_NR_BATCH) { @@ -405,15 +373,7 @@ restart: int retry = 0, err; while (!retry && transaction->t_checkpoint_list) { - struct buffer_head *bh; - jh = transaction->t_checkpoint_list; - bh = jh2bh(jh); - if (!jbd_trylock_bh_state(bh)) { - jbd_sync_bh(journal, bh); - retry = 1; - break; - } retry = __process_buffer(journal, jh, &batch_count, transaction); if (retry < 0 && !result) @@ -529,15 +489,12 @@ static int journal_clean_one_cp_list(struct journal_head *jh, int *released) do { jh = next_jh; next_jh = jh->b_cpnext; - /* Use trylock because of the ranking */ - if (jbd_trylock_bh_state(jh2bh(jh))) { - ret = __try_to_free_cp_buf(jh); - if (ret) { - freed++; - if (ret == 2) { - *released = 1; - return freed; - } + ret = __try_to_free_cp_buf(jh); + if (ret) { + freed++; + if (ret == 2) { + *released = 1; + return freed; } } /* @@ -620,9 +577,7 @@ out: * The function can free jh and bh. * * This function is called with j_list_lock held. - * This function is called with jbd_lock_bh_state(jh2bh(jh)) */ - int __jbd2_journal_remove_checkpoint(struct journal_head *jh) { struct transaction_chp_stats_s *stats; diff --git a/include/linux/journal-head.h b/include/linux/journal-head.h index 423cb6d78ee0..c18b46f8aeeb 100644 --- a/include/linux/journal-head.h +++ b/include/linux/journal-head.h @@ -66,6 +66,8 @@ struct journal_head { * transaction (if there is one). Only applies to buffers on a * transaction's data or metadata journaling list. * [j_list_lock] [jbd_lock_bh_state()] + * Either of these locks is enough for reading, both are needed for + * changes. */ transaction_t *b_transaction; -- cgit v1.2.3 From 3339578f05787259917788f461f4196b7349c2a4 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Tue, 13 Mar 2012 22:45:38 -0400 Subject: jbd2: cleanup journal tail after transaction commit Normally, we have to issue a cache flush before we can update journal tail in journal superblock, effectively wiping out old transactions from the journal. So use the fact that during transaction commit we issue cache flush anyway and opportunistically push journal tail as far as we can. Since update of journal superblock is still costly (we have to use WRITE_FUA), we update log tail only if we can free significant amount of space. Signed-off-by: Jan Kara Signed-off-by: "Theodore Ts'o" --- fs/jbd2/commit.c | 32 ++++++++++++++++++++++++++++++++ fs/jbd2/journal.c | 13 +++++++++++++ include/linux/jbd2.h | 1 + 3 files changed, 46 insertions(+) (limited to 'include/linux') diff --git a/fs/jbd2/commit.c b/fs/jbd2/commit.c index b89ef84786a7..1dfcb207ea69 100644 --- a/fs/jbd2/commit.c +++ b/fs/jbd2/commit.c @@ -331,6 +331,10 @@ void jbd2_journal_commit_transaction(journal_t *journal) struct buffer_head *cbh = NULL; /* For transactional checksums */ __u32 crc32_sum = ~0; struct blk_plug plug; + /* Tail of the journal */ + unsigned long first_block; + tid_t first_tid; + int update_tail; /* * First job: lock down the current transaction and wait for @@ -688,10 +692,30 @@ start_journal_io: err = 0; } + /* + * Get current oldest transaction in the log before we issue flush + * to the filesystem device. After the flush we can be sure that + * blocks of all older transactions are checkpointed to persistent + * storage and we will be safe to update journal start in the + * superblock with the numbers we get here. + */ + update_tail = + jbd2_journal_get_log_tail(journal, &first_tid, &first_block); + write_lock(&journal->j_state_lock); + if (update_tail) { + long freed = first_block - journal->j_tail; + + if (first_block < journal->j_tail) + freed += journal->j_last - journal->j_first; + /* Update tail only if we free significant amount of space */ + if (freed < journal->j_maxlen / 4) + update_tail = 0; + } J_ASSERT(commit_transaction->t_state == T_COMMIT); commit_transaction->t_state = T_COMMIT_DFLUSH; write_unlock(&journal->j_state_lock); + /* * If the journal is not located on the file system device, * then we must flush the file system device before we issue @@ -842,6 +866,14 @@ wait_for_iobuf: if (err) jbd2_journal_abort(journal, err); + /* + * Now disk caches for filesystem device are flushed so we are safe to + * erase checkpointed transactions from the log by updating journal + * superblock. + */ + if (update_tail) + jbd2_update_log_tail(journal, first_tid, first_block); + /* End of a transaction! Finally, we can do checkpoint processing: any buffers committed as a result of this transaction can be removed from any checkpoint list it was on diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c index c5ff177400ff..bda564f63864 100644 --- a/fs/jbd2/journal.c +++ b/fs/jbd2/journal.c @@ -821,6 +821,19 @@ void __jbd2_update_log_tail(journal_t *journal, tid_t tid, unsigned long block) write_unlock(&journal->j_state_lock); } +/* + * This is a variaon of __jbd2_update_log_tail which checks for validity of + * provided log tail and locks j_checkpoint_mutex. So it is safe against races + * with other threads updating log tail. + */ +void jbd2_update_log_tail(journal_t *journal, tid_t tid, unsigned long block) +{ + mutex_lock(&journal->j_checkpoint_mutex); + if (tid_gt(tid, journal->j_tail_sequence)) + __jbd2_update_log_tail(journal, tid, block); + mutex_unlock(&journal->j_checkpoint_mutex); +} + struct jbd2_stats_proc_session { journal_t *journal; struct transaction_stats_s *stats; diff --git a/include/linux/jbd2.h b/include/linux/jbd2.h index 876a7d87192b..912c30a8ddb1 100644 --- a/include/linux/jbd2.h +++ b/include/linux/jbd2.h @@ -974,6 +974,7 @@ int jbd2_journal_next_log_block(journal_t *, unsigned long long *); int jbd2_journal_get_log_tail(journal_t *journal, tid_t *tid, unsigned long *block); void __jbd2_update_log_tail(journal_t *journal, tid_t tid, unsigned long block); +void jbd2_update_log_tail(journal_t *journal, tid_t tid, unsigned long block); /* Commit management */ extern void jbd2_journal_commit_transaction(journal_t *); -- cgit v1.2.3 From 2cd36877ad1c61429e00c099b6903ebcd936ca00 Mon Sep 17 00:00:00 2001 From: Olof Johansson Date: Tue, 13 Mar 2012 21:35:51 -0700 Subject: Input: of_keymap - add device tree bindings for simple key matrices This adds a simple device tree binding for simple key matrix data and a helper to fill in the platform data. Signed-off-by: Olof Johansson Acked-by: Stephen Warren Signed-off-by: Dmitry Torokhov --- .../devicetree/bindings/input/matrix-keymap.txt | 19 +++++ drivers/input/Kconfig | 4 + drivers/input/Makefile | 1 + drivers/input/keyboard/Kconfig | 1 + drivers/input/of_keymap.c | 87 ++++++++++++++++++++++ include/linux/input/matrix_keypad.h | 19 +++++ 6 files changed, 131 insertions(+) create mode 100644 Documentation/devicetree/bindings/input/matrix-keymap.txt create mode 100644 drivers/input/of_keymap.c (limited to 'include/linux') diff --git a/Documentation/devicetree/bindings/input/matrix-keymap.txt b/Documentation/devicetree/bindings/input/matrix-keymap.txt new file mode 100644 index 000000000000..3cd8b98ccd2d --- /dev/null +++ b/Documentation/devicetree/bindings/input/matrix-keymap.txt @@ -0,0 +1,19 @@ +A simple common binding for matrix-connected key boards. Currently targeted at +defining the keys in the scope of linux key codes since that is a stable and +standardized interface at this time. + +Required properties: +- linux,keymap: an array of packed 1-cell entries containing the equivalent + of row, column and linux key-code. The 32-bit big endian cell is packed + as: + row << 24 | column << 16 | key-code + +Optional properties: +Some users of this binding might choose to specify secondary keymaps for +cases where there is a modifier key such as a Fn key. Proposed names +for said properties are "linux,fn-keymap" or with another descriptive +word for the modifier other from "Fn". + +Example: + linux,keymap = < 0x00030012 + 0x0102003a >; diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig index 001b147c7f95..332597980817 100644 --- a/drivers/input/Kconfig +++ b/drivers/input/Kconfig @@ -25,6 +25,10 @@ config INPUT if INPUT +config INPUT_OF_MATRIX_KEYMAP + depends on USE_OF + bool + config INPUT_FF_MEMLESS tristate "Support for memoryless force-feedback devices" help diff --git a/drivers/input/Makefile b/drivers/input/Makefile index 0c789490e0b3..b173a13a73ca 100644 --- a/drivers/input/Makefile +++ b/drivers/input/Makefile @@ -24,3 +24,4 @@ obj-$(CONFIG_INPUT_TOUCHSCREEN) += touchscreen/ obj-$(CONFIG_INPUT_MISC) += misc/ obj-$(CONFIG_INPUT_APMPOWER) += apm-power.o +obj-$(CONFIG_INPUT_OF_MATRIX_KEYMAP) += of_keymap.o diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index cdc385b2cf7d..3371954c979b 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -394,6 +394,7 @@ config KEYBOARD_NOMADIK config KEYBOARD_TEGRA tristate "NVIDIA Tegra internal matrix keyboard controller support" depends on ARCH_TEGRA + select INPUT_OF_MATRIX_KEYMAP if USE_OF help Say Y here if you want to use a matrix keyboard connected directly to the internal keyboard controller on Tegra SoCs. diff --git a/drivers/input/of_keymap.c b/drivers/input/of_keymap.c new file mode 100644 index 000000000000..061493d57682 --- /dev/null +++ b/drivers/input/of_keymap.c @@ -0,0 +1,87 @@ +/* + * Helpers for open firmware matrix keyboard bindings + * + * Copyright (C) 2012 Google, Inc + * + * Author: + * Olof Johansson + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +struct matrix_keymap_data * +matrix_keyboard_of_fill_keymap(struct device_node *np, + const char *propname) +{ + struct matrix_keymap_data *kd; + u32 *keymap; + int proplen, i; + const __be32 *prop; + + if (!np) + return NULL; + + if (!propname) + propname = "linux,keymap"; + + prop = of_get_property(np, propname, &proplen); + if (!prop) + return NULL; + + if (proplen % sizeof(u32)) { + pr_warn("Malformed keymap property %s in %s\n", + propname, np->full_name); + return NULL; + } + + kd = kzalloc(sizeof(*kd), GFP_KERNEL); + if (!kd) + return NULL; + + kd->keymap = keymap = kzalloc(proplen, GFP_KERNEL); + if (!kd->keymap) { + kfree(kd); + return NULL; + } + + kd->keymap_size = proplen / sizeof(u32); + + for (i = 0; i < kd->keymap_size; i++) { + u32 tmp = be32_to_cpup(prop + i); + int key_code, row, col; + + row = (tmp >> 24) & 0xff; + col = (tmp >> 16) & 0xff; + key_code = tmp & 0xffff; + keymap[i] = KEY(row, col, key_code); + } + + return kd; +} +EXPORT_SYMBOL_GPL(matrix_keyboard_of_fill_keymap); + +void matrix_keyboard_of_free_keymap(const struct matrix_keymap_data *kd) +{ + if (kd) { + kfree(kd->keymap); + kfree(kd); + } +} +EXPORT_SYMBOL_GPL(matrix_keyboard_of_free_keymap); diff --git a/include/linux/input/matrix_keypad.h b/include/linux/input/matrix_keypad.h index fe7c4b9ae270..6c07ced0af81 100644 --- a/include/linux/input/matrix_keypad.h +++ b/include/linux/input/matrix_keypad.h @@ -3,6 +3,7 @@ #include #include +#include #define MATRIX_MAX_ROWS 32 #define MATRIX_MAX_COLS 32 @@ -106,4 +107,22 @@ matrix_keypad_build_keymap(const struct matrix_keymap_data *keymap_data, __clear_bit(KEY_RESERVED, keybit); } +#ifdef CONFIG_INPUT_OF_MATRIX_KEYMAP +struct matrix_keymap_data * +matrix_keyboard_of_fill_keymap(struct device_node *np, const char *propname); + +void matrix_keyboard_of_free_keymap(const struct matrix_keymap_data *kd); +#else +static inline struct matrix_keymap_data * +matrix_keyboard_of_fill_keymap(struct device_node *np, const char *propname) +{ + return NULL; +} + +static inline void +matrix_keyboard_of_free_keymap(const struct matrix_keymap_data *kd) +{ +} +#endif + #endif /* _MATRIX_KEYPAD_H */ -- cgit v1.2.3 From a7b422cda5084db7265c3b23310a959b43b47529 Mon Sep 17 00:00:00 2001 From: Konrad Rzeszutek Wilk Date: Tue, 13 Mar 2012 19:18:39 -0400 Subject: provide disable_cpufreq() function to disable the API. useful for disabling cpufreq altogether. The cpu frequency scaling drivers and cpu frequency governors will fail to register. Signed-off-by: Konrad Rzeszutek Wilk Signed-off-by: Dave Jones --- drivers/cpufreq/cpufreq.c | 24 ++++++++++++++++++++++++ include/linux/cpufreq.h | 2 ++ 2 files changed, 26 insertions(+) (limited to 'include/linux') diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 622013fb7890..7f2f149ae40f 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -126,6 +126,15 @@ static int __init init_cpufreq_transition_notifier_list(void) } pure_initcall(init_cpufreq_transition_notifier_list); +static int off __read_mostly; +int cpufreq_disabled(void) +{ + return off; +} +void disable_cpufreq(void) +{ + off = 1; +} static LIST_HEAD(cpufreq_governor_list); static DEFINE_MUTEX(cpufreq_governor_mutex); @@ -1441,6 +1450,9 @@ int __cpufreq_driver_target(struct cpufreq_policy *policy, { int retval = -EINVAL; + if (cpufreq_disabled()) + return -ENODEV; + pr_debug("target for CPU %u: %u kHz, relation %u\n", policy->cpu, target_freq, relation); if (cpu_online(policy->cpu) && cpufreq_driver->target) @@ -1549,6 +1561,9 @@ int cpufreq_register_governor(struct cpufreq_governor *governor) if (!governor) return -EINVAL; + if (cpufreq_disabled()) + return -ENODEV; + mutex_lock(&cpufreq_governor_mutex); err = -EBUSY; @@ -1572,6 +1587,9 @@ void cpufreq_unregister_governor(struct cpufreq_governor *governor) if (!governor) return; + if (cpufreq_disabled()) + return; + #ifdef CONFIG_HOTPLUG_CPU for_each_present_cpu(cpu) { if (cpu_online(cpu)) @@ -1814,6 +1832,9 @@ int cpufreq_register_driver(struct cpufreq_driver *driver_data) unsigned long flags; int ret; + if (cpufreq_disabled()) + return -ENODEV; + if (!driver_data || !driver_data->verify || !driver_data->init || ((!driver_data->setpolicy) && (!driver_data->target))) return -EINVAL; @@ -1901,6 +1922,9 @@ static int __init cpufreq_core_init(void) { int cpu; + if (cpufreq_disabled()) + return -ENODEV; + for_each_possible_cpu(cpu) { per_cpu(cpufreq_policy_cpu, cpu) = -1; init_rwsem(&per_cpu(cpu_policy_rwsem, cpu)); diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index 6216115c7789..8ff442753c75 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -35,6 +35,7 @@ #ifdef CONFIG_CPU_FREQ int cpufreq_register_notifier(struct notifier_block *nb, unsigned int list); int cpufreq_unregister_notifier(struct notifier_block *nb, unsigned int list); +extern void disable_cpufreq(void); #else /* CONFIG_CPU_FREQ */ static inline int cpufreq_register_notifier(struct notifier_block *nb, unsigned int list) @@ -46,6 +47,7 @@ static inline int cpufreq_unregister_notifier(struct notifier_block *nb, { return 0; } +static inline void disable_cpufreq(void) { } #endif /* CONFIG_CPU_FREQ */ /* if (cpufreq_driver->target) exists, the ->governor decides what frequency -- cgit v1.2.3 From 313162d0b83836e2f57e51b9b8650fb4b9c396ea Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Mon, 30 Jan 2012 11:46:54 -0500 Subject: device.h: audit and cleanup users in main include dir The header includes a lot of stuff, and it in turn gets a lot of use just for the basic "struct device" which appears so often. Clean up the users as follows: 1) For those headers only needing "struct device" as a pointer in fcn args, replace the include with exactly that. 2) For headers not really using anything from device.h, simply delete the include altogether. 3) For headers relying on getting device.h implicitly before being included themselves, now explicitly include device.h 4) For files in which doing #1 or #2 uncovers an implicit dependency on some other header, fix by explicitly adding the required header(s). Any C files that were implicitly relying on device.h to be present have already been dealt with in advance. Total removals from #1 and #2: 51. Total additions coming from #3: 9. Total other implicit dependencies from #4: 7. As of 3.3-rc1, there were 110, so a net removal of 42 gives about a 38% reduction in device.h presence in include/* Signed-off-by: Paul Gortmaker --- include/linux/amba/pl022.h | 2 -- include/linux/atmdev.h | 2 +- include/linux/attribute_container.h | 3 ++- include/linux/c2port.h | 3 ++- include/linux/cdrom.h | 1 - include/linux/cpu.h | 3 ++- include/linux/cpufreq.h | 1 - include/linux/crash_dump.h | 1 - include/linux/dma-buf.h | 2 +- include/linux/edac.h | 6 +++++- include/linux/fb.h | 1 - include/linux/firewire.h | 3 ++- include/linux/hwmon-sysfs.h | 2 ++ include/linux/hwmon.h | 2 +- include/linux/hwspinlock.h | 2 +- include/linux/ide.h | 3 ++- include/linux/ipmi.h | 2 +- include/linux/ipmi_smi.h | 3 ++- include/linux/jz4740-adc.h | 2 +- include/linux/maple.h | 2 +- include/linux/mfd/abx500.h | 3 ++- include/linux/mfd/abx500/ab5500.h | 2 +- include/linux/mfd/abx500/ab8500.h | 4 +++- include/linux/mfd/pm8xxx/pm8921.h | 1 - include/linux/mfd/stmpe.h | 4 +++- include/linux/mfd/tc3589x.h | 2 +- include/linux/mlx4/driver.h | 1 - include/linux/mmc/card.h | 1 + include/linux/mmc/core.h | 2 +- include/linux/mmc/host.h | 1 + include/linux/netdevice.h | 2 +- include/linux/of_device.h | 3 ++- include/linux/opp.h | 1 + include/linux/phy.h | 5 +++-- include/linux/pm_domain.h | 2 ++ include/linux/power_supply.h | 3 ++- include/linux/regmap.h | 2 +- include/linux/regulator/consumer.h | 3 ++- include/linux/rfkill.h | 2 +- include/linux/rio_drv.h | 1 - include/linux/serial_pnx8xxx.h | 1 - include/linux/spi/mmc_spi.h | 2 +- include/linux/wimax/debug.h | 2 +- include/media/media-device.h | 3 ++- include/media/v4l2-ctrls.h | 1 - include/media/v4l2-ioctl.h | 1 - include/net/mac80211.h | 3 ++- include/scsi/scsi_device.h | 2 +- include/sound/core.h | 5 ++--- include/sound/soc-dapm.h | 3 ++- include/trace/events/regmap.h | 2 +- include/trace/events/rpm.h | 3 ++- include/trace/events/writeback.h | 1 - 53 files changed, 68 insertions(+), 52 deletions(-) (limited to 'include/linux') diff --git a/include/linux/amba/pl022.h b/include/linux/amba/pl022.h index 572f637299c9..9da37a45faf5 100644 --- a/include/linux/amba/pl022.h +++ b/include/linux/amba/pl022.h @@ -25,8 +25,6 @@ #ifndef _SSP_PL022_H #define _SSP_PL022_H -#include - /** * whether SSP is in loopback mode or not */ diff --git a/include/linux/atmdev.h b/include/linux/atmdev.h index f4ff882cb2da..52c940935bd1 100644 --- a/include/linux/atmdev.h +++ b/include/linux/atmdev.h @@ -213,7 +213,6 @@ struct atm_cirange { #ifdef __KERNEL__ -#include #include /* wait_queue_head_t */ #include /* struct timeval */ #include @@ -249,6 +248,7 @@ struct k_atm_dev_stats { struct k_atm_aal_stats aal5; }; +struct device; enum { ATM_VF_ADDR, /* Address is in use. Set by anybody, cleared diff --git a/include/linux/attribute_container.h b/include/linux/attribute_container.h index c3ab81428c66..896c6892f327 100644 --- a/include/linux/attribute_container.h +++ b/include/linux/attribute_container.h @@ -9,10 +9,11 @@ #ifndef _ATTRIBUTE_CONTAINER_H_ #define _ATTRIBUTE_CONTAINER_H_ -#include #include #include +struct device; + struct attribute_container { struct list_head node; struct klist containers; diff --git a/include/linux/c2port.h b/include/linux/c2port.h index a2f7d7413f30..4efabcb51347 100644 --- a/include/linux/c2port.h +++ b/include/linux/c2port.h @@ -9,11 +9,12 @@ * the Free Software Foundation */ -#include #include #define C2PORT_NAME_LEN 32 +struct device; + /* * C2 port basic structs */ diff --git a/include/linux/cdrom.h b/include/linux/cdrom.h index 35eae4b67503..d35a12fbd8f1 100644 --- a/include/linux/cdrom.h +++ b/include/linux/cdrom.h @@ -910,7 +910,6 @@ struct mode_page_header { #ifdef __KERNEL__ #include /* not really needed, later.. */ -#include #include struct packet_command diff --git a/include/linux/cpu.h b/include/linux/cpu.h index 1f6587590a1a..e3cdc3aec87e 100644 --- a/include/linux/cpu.h +++ b/include/linux/cpu.h @@ -14,11 +14,12 @@ #ifndef _LINUX_CPU_H_ #define _LINUX_CPU_H_ -#include #include #include #include +struct device; + struct cpu { int node_id; /* The node which contains the CPU */ int hotpluggable; /* creates sysfs control file if hotpluggable */ diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index 6216115c7789..fad1382f8615 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include diff --git a/include/linux/crash_dump.h b/include/linux/crash_dump.h index b936763f2236..37e4f8da7cdf 100644 --- a/include/linux/crash_dump.h +++ b/include/linux/crash_dump.h @@ -3,7 +3,6 @@ #ifdef CONFIG_CRASH_DUMP #include -#include #include #include diff --git a/include/linux/dma-buf.h b/include/linux/dma-buf.h index f8ac076afa52..887dcd487062 100644 --- a/include/linux/dma-buf.h +++ b/include/linux/dma-buf.h @@ -26,11 +26,11 @@ #include #include -#include #include #include #include +struct device; struct dma_buf; struct dma_buf_attachment; diff --git a/include/linux/edac.h b/include/linux/edac.h index 1cd3947987e5..ba317e2930a1 100644 --- a/include/linux/edac.h +++ b/include/linux/edac.h @@ -13,7 +13,11 @@ #define _LINUX_EDAC_H_ #include -#include +#include +#include +#include + +struct device; #define EDAC_OPSTATE_INVAL -1 #define EDAC_OPSTATE_POLL 0 diff --git a/include/linux/fb.h b/include/linux/fb.h index c18122f40543..9d509675034a 100644 --- a/include/linux/fb.h +++ b/include/linux/fb.h @@ -407,7 +407,6 @@ struct fb_cursor { #include #include -#include #include #include #include diff --git a/include/linux/firewire.h b/include/linux/firewire.h index 84ccf8e04fa6..fba45b89689e 100644 --- a/include/linux/firewire.h +++ b/include/linux/firewire.h @@ -2,7 +2,6 @@ #define _LINUX_FIREWIRE_H #include -#include #include #include #include @@ -68,6 +67,8 @@ #define CSR_MODEL 0x17 #define CSR_DIRECTORY_ID 0x20 +struct device; + struct fw_csr_iterator { const u32 *p; const u32 *end; diff --git a/include/linux/hwmon-sysfs.h b/include/linux/hwmon-sysfs.h index a90c09d331c1..1c7b89ae6bdc 100644 --- a/include/linux/hwmon-sysfs.h +++ b/include/linux/hwmon-sysfs.h @@ -20,6 +20,8 @@ #ifndef _LINUX_HWMON_SYSFS_H #define _LINUX_HWMON_SYSFS_H +#include + struct sensor_device_attribute{ struct device_attribute dev_attr; int index; diff --git a/include/linux/hwmon.h b/include/linux/hwmon.h index 6b6ee702b007..82b29ae6ebb0 100644 --- a/include/linux/hwmon.h +++ b/include/linux/hwmon.h @@ -14,7 +14,7 @@ #ifndef _HWMON_H_ #define _HWMON_H_ -#include +struct device; struct device *hwmon_device_register(struct device *dev); diff --git a/include/linux/hwspinlock.h b/include/linux/hwspinlock.h index aad6bd4b3efd..3343298e40e8 100644 --- a/include/linux/hwspinlock.h +++ b/include/linux/hwspinlock.h @@ -20,12 +20,12 @@ #include #include -#include /* hwspinlock mode argument */ #define HWLOCK_IRQSTATE 0x01 /* Disable interrupts, save state */ #define HWLOCK_IRQ 0x02 /* Disable interrupts, don't save state */ +struct device; struct hwspinlock; struct hwspinlock_device; struct hwspinlock_ops; diff --git a/include/linux/ide.h b/include/linux/ide.h index 501370b61ee5..7afe15f916da 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include @@ -43,6 +42,8 @@ #define ERROR_RESET 3 /* Reset controller every 4th retry */ #define ERROR_RECAL 1 /* Recalibrate every 2nd retry */ +struct device; + /* Error codes returned in rq->errors to the higher part of the driver. */ enum { IDE_DRV_ERROR_GENERAL = 101, diff --git a/include/linux/ipmi.h b/include/linux/ipmi.h index bbd156bb953b..48dcba9b2065 100644 --- a/include/linux/ipmi.h +++ b/include/linux/ipmi.h @@ -220,10 +220,10 @@ struct kernel_ipmi_msg { * The in-kernel interface. */ #include -#include #include struct module; +struct device; /* Opaque type for a IPMI message user. One of these is needed to send and receive messages. */ diff --git a/include/linux/ipmi_smi.h b/include/linux/ipmi_smi.h index 3ef0d8b6aa6f..fcb5d44ea635 100644 --- a/include/linux/ipmi_smi.h +++ b/include/linux/ipmi_smi.h @@ -36,10 +36,11 @@ #include #include -#include #include #include +struct device; + /* This files describes the interface for IPMI system management interface drivers to bind into the IPMI message handler. */ diff --git a/include/linux/jz4740-adc.h b/include/linux/jz4740-adc.h index 9053f95e9687..8184578fbfa4 100644 --- a/include/linux/jz4740-adc.h +++ b/include/linux/jz4740-adc.h @@ -2,7 +2,7 @@ #ifndef __LINUX_JZ4740_ADC #define __LINUX_JZ4740_ADC -#include +struct device; /* * jz4740_adc_set_config - Configure a JZ4740 adc device diff --git a/include/linux/maple.h b/include/linux/maple.h index d9a51b9b3300..c37288b23e0c 100644 --- a/include/linux/maple.h +++ b/include/linux/maple.h @@ -1,9 +1,9 @@ #ifndef __LINUX_MAPLE_H #define __LINUX_MAPLE_H -#include #include +struct device; extern struct bus_type maple_bus_type; /* Maple Bus command and response codes */ diff --git a/include/linux/mfd/abx500.h b/include/linux/mfd/abx500.h index 9970337ff041..e20dd6ead1d0 100644 --- a/include/linux/mfd/abx500.h +++ b/include/linux/mfd/abx500.h @@ -14,9 +14,10 @@ * Author: Rickard Andersson */ -#include #include +struct device; + #ifndef MFD_ABX500_H #define MFD_ABX500_H diff --git a/include/linux/mfd/abx500/ab5500.h b/include/linux/mfd/abx500/ab5500.h index a720051ae933..54f820ed73bb 100644 --- a/include/linux/mfd/abx500/ab5500.h +++ b/include/linux/mfd/abx500/ab5500.h @@ -6,7 +6,7 @@ #ifndef MFD_AB5500_H #define MFD_AB5500_H -#include +struct device; enum ab5500_devid { AB5500_DEVID_ADC, diff --git a/include/linux/mfd/abx500/ab8500.h b/include/linux/mfd/abx500/ab8500.h index 838c6b487cc5..dca94396190d 100644 --- a/include/linux/mfd/abx500/ab8500.h +++ b/include/linux/mfd/abx500/ab8500.h @@ -7,7 +7,9 @@ #ifndef MFD_AB8500_H #define MFD_AB8500_H -#include +#include + +struct device; /* * AB8500 bank addresses diff --git a/include/linux/mfd/pm8xxx/pm8921.h b/include/linux/mfd/pm8xxx/pm8921.h index d5517fd32d1b..00fa3de7659d 100644 --- a/include/linux/mfd/pm8xxx/pm8921.h +++ b/include/linux/mfd/pm8xxx/pm8921.h @@ -18,7 +18,6 @@ #ifndef __MFD_PM8921_H #define __MFD_PM8921_H -#include #include #define PM8921_NR_IRQS 256 diff --git a/include/linux/mfd/stmpe.h b/include/linux/mfd/stmpe.h index ca1d7a347600..8c54de674b4b 100644 --- a/include/linux/mfd/stmpe.h +++ b/include/linux/mfd/stmpe.h @@ -8,7 +8,9 @@ #ifndef __LINUX_MFD_STMPE_H #define __LINUX_MFD_STMPE_H -#include +#include + +struct device; enum stmpe_block { STMPE_BLOCK_GPIO = 1 << 0, diff --git a/include/linux/mfd/tc3589x.h b/include/linux/mfd/tc3589x.h index 16c76e124f9c..3acb3a8e3af5 100644 --- a/include/linux/mfd/tc3589x.h +++ b/include/linux/mfd/tc3589x.h @@ -7,7 +7,7 @@ #ifndef __LINUX_MFD_TC3589x_H #define __LINUX_MFD_TC3589x_H -#include +struct device; enum tx3589x_block { TC3589x_BLOCK_GPIO = 1 << 0, diff --git a/include/linux/mlx4/driver.h b/include/linux/mlx4/driver.h index e1eebf78caba..5f1298b1b5ef 100644 --- a/include/linux/mlx4/driver.h +++ b/include/linux/mlx4/driver.h @@ -33,7 +33,6 @@ #ifndef MLX4_DRIVER_H #define MLX4_DRIVER_H -#include #include struct mlx4_dev; diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 9f22ba572de0..895978c9a16b 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -10,6 +10,7 @@ #ifndef LINUX_MMC_CARD_H #define LINUX_MMC_CARD_H +#include #include #include diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h index 87a976cc5654..2e6a681fceb2 100644 --- a/include/linux/mmc/core.h +++ b/include/linux/mmc/core.h @@ -9,7 +9,7 @@ #define LINUX_MMC_CORE_H #include -#include +#include struct request; struct mmc_data; diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 0beba1e5e1ed..68558d743da5 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -12,6 +12,7 @@ #include #include +#include #include #include diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 0eac07c95255..fa568da2fcda 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -38,7 +38,6 @@ #include #include -#include #include #include #include @@ -56,6 +55,7 @@ #include struct netpoll_info; +struct device; struct phy_device; /* 802.11 specific */ struct wireless_dev; diff --git a/include/linux/of_device.h b/include/linux/of_device.h index ae5638480ef2..b444adf692dd 100644 --- a/include/linux/of_device.h +++ b/include/linux/of_device.h @@ -5,10 +5,11 @@ #include /* temporary until merge */ #ifdef CONFIG_OF_DEVICE -#include #include #include +struct device; + extern const struct of_device_id *of_match_device( const struct of_device_id *matches, const struct device *dev); extern void of_device_make_bus_id(struct device *dev); diff --git a/include/linux/opp.h b/include/linux/opp.h index ee94b33080c2..2a4e5faee904 100644 --- a/include/linux/opp.h +++ b/include/linux/opp.h @@ -19,6 +19,7 @@ #include struct opp; +struct device; enum opp_event { OPP_EVENT_ADD, OPP_EVENT_ENABLE, OPP_EVENT_DISABLE, diff --git a/include/linux/phy.h b/include/linux/phy.h index c599f7eca1e7..6fe0a37d4abf 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -19,7 +19,6 @@ #define __PHY_H #include -#include #include #include #include @@ -88,6 +87,9 @@ typedef enum { IEEE 802.3ae clause 45 addressing mode used by 10GIGE phy chips. */ #define MII_ADDR_C45 (1<<30) +struct device; +struct sk_buff; + /* * The Bus class for PHYs. Devices which provide access to * PHYs should register using this structure @@ -241,7 +243,6 @@ enum phy_state { PHY_RESUMING }; -struct sk_buff; /* phy_device: An instance of a PHY * diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h index a03a0ad998b8..fa2e20b621b0 100644 --- a/include/linux/pm_domain.h +++ b/include/linux/pm_domain.h @@ -10,6 +10,8 @@ #define _LINUX_PM_DOMAIN_H #include +#include +#include #include enum gpd_status { diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index fa9b962aec12..c38c13db8832 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -13,10 +13,11 @@ #ifndef __LINUX_POWER_SUPPLY_H__ #define __LINUX_POWER_SUPPLY_H__ -#include #include #include +struct device; + /* * All voltages, currents, charges, energies, time and temperatures in uV, * µA, µAh, µWh, seconds and tenths of degree Celsius unless otherwise diff --git a/include/linux/regmap.h b/include/linux/regmap.h index eb93921cdd30..765736cbb509 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -13,10 +13,10 @@ * published by the Free Software Foundation. */ -#include #include struct module; +struct device; struct i2c_client; struct spi_device; diff --git a/include/linux/regulator/consumer.h b/include/linux/regulator/consumer.h index f2698a0edfc4..10e0d33e8ff7 100644 --- a/include/linux/regulator/consumer.h +++ b/include/linux/regulator/consumer.h @@ -35,7 +35,8 @@ #ifndef __LINUX_REGULATOR_CONSUMER_H_ #define __LINUX_REGULATOR_CONSUMER_H_ -#include +struct device; +struct notifier_block; /* * Regulator operating modes. diff --git a/include/linux/rfkill.h b/include/linux/rfkill.h index c6c608482cba..6fdf02737e9d 100644 --- a/include/linux/rfkill.h +++ b/include/linux/rfkill.h @@ -117,10 +117,10 @@ enum rfkill_user_states { #include #include #include -#include #include #include +struct device; /* this is opaque */ struct rfkill; diff --git a/include/linux/rio_drv.h b/include/linux/rio_drv.h index 229b3ca23134..7f07470e1ed9 100644 --- a/include/linux/rio_drv.h +++ b/include/linux/rio_drv.h @@ -17,7 +17,6 @@ #include #include #include -#include #include #include diff --git a/include/linux/serial_pnx8xxx.h b/include/linux/serial_pnx8xxx.h index de6c19c7f340..79ad87b0be3e 100644 --- a/include/linux/serial_pnx8xxx.h +++ b/include/linux/serial_pnx8xxx.h @@ -20,7 +20,6 @@ #define _LINUX_SERIAL_PNX8XXX_H #include -#include #define PNX8XXX_NR_PORTS 2 diff --git a/include/linux/spi/mmc_spi.h b/include/linux/spi/mmc_spi.h index 0f4eb165f254..32be8dbdf191 100644 --- a/include/linux/spi/mmc_spi.h +++ b/include/linux/spi/mmc_spi.h @@ -1,10 +1,10 @@ #ifndef __LINUX_SPI_MMC_SPI_H #define __LINUX_SPI_MMC_SPI_H -#include #include #include +struct device; struct mmc_host; /* Put this in platform_data of a device being used to manage an MMC/SD diff --git a/include/linux/wimax/debug.h b/include/linux/wimax/debug.h index 57031b4d12f2..aaf24ba12c4d 100644 --- a/include/linux/wimax/debug.h +++ b/include/linux/wimax/debug.h @@ -154,9 +154,9 @@ #define __debug__h__ #include -#include #include +struct device; /* Backend stuff */ diff --git a/include/media/media-device.h b/include/media/media-device.h index 6a27d916c250..eaade9815bb6 100644 --- a/include/media/media-device.h +++ b/include/media/media-device.h @@ -23,7 +23,6 @@ #ifndef _MEDIA_DEVICE_H #define _MEDIA_DEVICE_H -#include #include #include #include @@ -31,6 +30,8 @@ #include #include +struct device; + /** * struct media_device - Media device * @dev: Parent device diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h index eeb3df637144..62e04dda22f2 100644 --- a/include/media/v4l2-ctrls.h +++ b/include/media/v4l2-ctrls.h @@ -22,7 +22,6 @@ #define _V4L2_CTRLS_H #include -#include #include /* forward references */ diff --git a/include/media/v4l2-ioctl.h b/include/media/v4l2-ioctl.h index 3f5d60fc5df6..032ff21b28bb 100644 --- a/include/media/v4l2-ioctl.h +++ b/include/media/v4l2-ioctl.h @@ -11,7 +11,6 @@ #include #include -#include #include #include /* need __user */ #include diff --git a/include/net/mac80211.h b/include/net/mac80211.h index d49928ba5d09..f0605df39e95 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include @@ -87,6 +86,8 @@ * */ +struct device; + /** * enum ieee80211_max_queues - maximum number of queues * diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h index 77273f2fdd80..34dc8315d837 100644 --- a/include/scsi/scsi_device.h +++ b/include/scsi/scsi_device.h @@ -1,7 +1,6 @@ #ifndef _SCSI_SCSI_DEVICE_H #define _SCSI_SCSI_DEVICE_H -#include #include #include #include @@ -9,6 +8,7 @@ #include #include +struct device; struct request_queue; struct scsi_cmnd; struct scsi_lun; diff --git a/include/sound/core.h b/include/sound/core.h index 5ab255f196cc..a4207163697e 100644 --- a/include/sound/core.h +++ b/include/sound/core.h @@ -26,7 +26,6 @@ #include /* struct mutex */ #include /* struct rw_semaphore */ #include /* pm_message_t */ -#include #include /* number of supported soundcards */ @@ -39,10 +38,10 @@ #define CONFIG_SND_MAJOR 116 /* standard configuration */ /* forward declarations */ -#ifdef CONFIG_PCI struct pci_dev; -#endif struct module; +struct device; +struct device_attribute; /* device allocation stuff */ diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index d26a9b784772..ab0d84aeae1e 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -13,10 +13,11 @@ #ifndef __LINUX_SND_SOC_DAPM_H #define __LINUX_SND_SOC_DAPM_H -#include #include #include +struct device; + /* widget has no PM register bit */ #define SND_SOC_NOPM -1 diff --git a/include/trace/events/regmap.h b/include/trace/events/regmap.h index 12fbf43524e9..50fe9ba61442 100644 --- a/include/trace/events/regmap.h +++ b/include/trace/events/regmap.h @@ -4,10 +4,10 @@ #if !defined(_TRACE_REGMAP_H) || defined(TRACE_HEADER_MULTI_READ) #define _TRACE_REGMAP_H -#include #include #include +struct device; struct regmap; /* diff --git a/include/trace/events/rpm.h b/include/trace/events/rpm.h index d62c558bf64b..33f85b68c22c 100644 --- a/include/trace/events/rpm.h +++ b/include/trace/events/rpm.h @@ -7,7 +7,8 @@ #include #include -#include + +struct device; /* * The rpm_internal events are used for tracing some important diff --git a/include/trace/events/writeback.h b/include/trace/events/writeback.h index 8588a8918023..2d0dd3e03e41 100644 --- a/include/trace/events/writeback.h +++ b/include/trace/events/writeback.h @@ -5,7 +5,6 @@ #define _TRACE_WRITEBACK_H #include -#include #include #define show_inode_state(state) \ -- cgit v1.2.3 From 9e5ed094c89e55fbf11d2e81d60be98eb12346c0 Mon Sep 17 00:00:00 2001 From: viresh kumar Date: Thu, 15 Mar 2012 10:40:38 +0100 Subject: ARM: 7362/1: AMBA: Add module_amba_driver() helper macro for amba_driver For simple modules that contain a single amba_driver without any additional setup code then ends up being a block of duplicated boilerplate. This patch adds a new macro, module_amba_driver(), which replaces the module_init()/module_exit() registrations with template functions. Signed-off-by: Viresh Kumar Signed-off-by: Russell King --- drivers/char/hw_random/nomadik-rng.c | 13 +------------ drivers/dma/pl330.c | 13 +------------ drivers/input/serio/ambakmi.c | 13 +------------ drivers/mmc/host/mmci.c | 12 +----------- drivers/rtc/rtc-pl030.c | 13 +------------ drivers/rtc/rtc-pl031.c | 13 +------------ drivers/watchdog/sp805_wdt.c | 12 +----------- include/linux/amba/bus.h | 9 +++++++++ sound/arm/aaci.c | 13 +------------ 9 files changed, 17 insertions(+), 94 deletions(-) (limited to 'include/linux') diff --git a/drivers/char/hw_random/nomadik-rng.c b/drivers/char/hw_random/nomadik-rng.c index 3d3c1e6703b4..96de0249e595 100644 --- a/drivers/char/hw_random/nomadik-rng.c +++ b/drivers/char/hw_random/nomadik-rng.c @@ -107,17 +107,6 @@ static struct amba_driver nmk_rng_driver = { .id_table = nmk_rng_ids, }; -static int __init nmk_rng_init(void) -{ - return amba_driver_register(&nmk_rng_driver); -} - -static void __devexit nmk_rng_exit(void) -{ - amba_driver_unregister(&nmk_rng_driver); -} - -module_init(nmk_rng_init); -module_exit(nmk_rng_exit); +module_amba_driver(nmk_rng_driver); MODULE_LICENSE("GPL"); diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index b8ec03ee8e22..16b66c827f19 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -1035,18 +1035,7 @@ static struct amba_driver pl330_driver = { .remove = pl330_remove, }; -static int __init pl330_init(void) -{ - return amba_driver_register(&pl330_driver); -} -module_init(pl330_init); - -static void __exit pl330_exit(void) -{ - amba_driver_unregister(&pl330_driver); - return; -} -module_exit(pl330_exit); +module_amba_driver(pl330_driver); MODULE_AUTHOR("Jaswinder Singh "); MODULE_DESCRIPTION("API Driver for PL330 DMAC"); diff --git a/drivers/input/serio/ambakmi.c b/drivers/input/serio/ambakmi.c index 8407d5b0ced8..2ffd110bd5bc 100644 --- a/drivers/input/serio/ambakmi.c +++ b/drivers/input/serio/ambakmi.c @@ -208,18 +208,7 @@ static struct amba_driver ambakmi_driver = { .resume = amba_kmi_resume, }; -static int __init amba_kmi_init(void) -{ - return amba_driver_register(&ambakmi_driver); -} - -static void __exit amba_kmi_exit(void) -{ - amba_driver_unregister(&ambakmi_driver); -} - -module_init(amba_kmi_init); -module_exit(amba_kmi_exit); +module_amba_driver(ambakmi_driver); MODULE_AUTHOR("Russell King "); MODULE_DESCRIPTION("AMBA KMI controller driver"); diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 304f2f98b680..6692392c05dd 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -1519,18 +1519,8 @@ static struct amba_driver mmci_driver = { .id_table = mmci_ids, }; -static int __init mmci_init(void) -{ - return amba_driver_register(&mmci_driver); -} - -static void __exit mmci_exit(void) -{ - amba_driver_unregister(&mmci_driver); -} +module_amba_driver(mmci_driver); -module_init(mmci_init); -module_exit(mmci_exit); module_param(fmax, uint, 0444); MODULE_DESCRIPTION("ARM PrimeCell PL180/181 Multimedia Card Interface driver"); diff --git a/drivers/rtc/rtc-pl030.c b/drivers/rtc/rtc-pl030.c index 02111fee077e..b2d3d20baebc 100644 --- a/drivers/rtc/rtc-pl030.c +++ b/drivers/rtc/rtc-pl030.c @@ -185,18 +185,7 @@ static struct amba_driver pl030_driver = { .id_table = pl030_ids, }; -static int __init pl030_init(void) -{ - return amba_driver_register(&pl030_driver); -} - -static void __exit pl030_exit(void) -{ - amba_driver_unregister(&pl030_driver); -} - -module_init(pl030_init); -module_exit(pl030_exit); +module_amba_driver(pl030_driver); MODULE_AUTHOR("Russell King "); MODULE_DESCRIPTION("ARM AMBA PL030 RTC Driver"); diff --git a/drivers/rtc/rtc-pl031.c b/drivers/rtc/rtc-pl031.c index a952c8de1dd7..359e2c53c545 100644 --- a/drivers/rtc/rtc-pl031.c +++ b/drivers/rtc/rtc-pl031.c @@ -431,18 +431,7 @@ static struct amba_driver pl031_driver = { .remove = pl031_remove, }; -static int __init pl031_init(void) -{ - return amba_driver_register(&pl031_driver); -} - -static void __exit pl031_exit(void) -{ - amba_driver_unregister(&pl031_driver); -} - -module_init(pl031_init); -module_exit(pl031_exit); +module_amba_driver(pl031_driver); MODULE_AUTHOR("Deepak Saxena Date: Fri, 16 Mar 2012 12:28:22 -0700 Subject: Input: add driver support for MAX8997-haptic The MAX8997-haptic function can be used to control motor. User can control the haptic driver by using force feedback framework. Signed-off-by: Donggeun Kim Signed-off-by: MyungJoo Ham Signed-off-by: Kyungmin Park Acked-by: Samuel Ortiz Signed-off-by: Dmitry Torokhov --- drivers/input/misc/Kconfig | 12 ++ drivers/input/misc/Makefile | 1 + drivers/input/misc/max8997_haptic.c | 407 ++++++++++++++++++++++++++++++++++++ include/linux/mfd/max8997.h | 53 ++++- 4 files changed, 472 insertions(+), 1 deletion(-) create mode 100644 drivers/input/misc/max8997_haptic.c (limited to 'include/linux') diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index eb07e88162ad..d62827271802 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -134,6 +134,18 @@ config INPUT_MAX8925_ONKEY To compile this driver as a module, choose M here: the module will be called max8925_onkey. +config INPUT_MAX8997_HAPTIC + tristate "MAXIM MAX8997 haptic controller support" + depends on HAVE_PWM && MFD_MAX8997 + select INPUT_FF_MEMLESS + help + This option enables device driver support for the haptic controller + on MAXIM MAX8997 chip. This driver supports ff-memless interface + from input framework. + + To compile this driver as module, choose M here: the + module will be called max8997-haptic. + config INPUT_MC13783_PWRBUTTON tristate "MC13783 ON buttons" depends on MFD_MC13783 diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index a6d8de069148..f55cdf4916fa 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -31,6 +31,7 @@ obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o obj-$(CONFIG_INPUT_KXTJ9) += kxtj9.o obj-$(CONFIG_INPUT_M68K_BEEP) += m68kspkr.o obj-$(CONFIG_INPUT_MAX8925_ONKEY) += max8925_onkey.o +obj-$(CONFIG_INPUT_MAX8997_HAPTIC) += max8997_haptic.o obj-$(CONFIG_INPUT_MC13783_PWRBUTTON) += mc13783-pwrbutton.o obj-$(CONFIG_INPUT_MMA8450) += mma8450.o obj-$(CONFIG_INPUT_MPU3050) += mpu3050.o diff --git a/drivers/input/misc/max8997_haptic.c b/drivers/input/misc/max8997_haptic.c new file mode 100644 index 000000000000..05b7b8bfaf0a --- /dev/null +++ b/drivers/input/misc/max8997_haptic.c @@ -0,0 +1,407 @@ +/* + * MAX8997-haptic controller driver + * + * Copyright (C) 2012 Samsung Electronics + * Donggeun Kim + * + * This program is not provided / owned by Maxim Integrated Products. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Haptic configuration 2 register */ +#define MAX8997_MOTOR_TYPE_SHIFT 7 +#define MAX8997_ENABLE_SHIFT 6 +#define MAX8997_MODE_SHIFT 5 + +/* Haptic driver configuration register */ +#define MAX8997_CYCLE_SHIFT 6 +#define MAX8997_SIG_PERIOD_SHIFT 4 +#define MAX8997_SIG_DUTY_SHIFT 2 +#define MAX8997_PWM_DUTY_SHIFT 0 + +struct max8997_haptic { + struct device *dev; + struct i2c_client *client; + struct input_dev *input_dev; + struct regulator *regulator; + + struct work_struct work; + struct mutex mutex; + + bool enabled; + unsigned int level; + + struct pwm_device *pwm; + unsigned int pwm_period; + enum max8997_haptic_pwm_divisor pwm_divisor; + + enum max8997_haptic_motor_type type; + enum max8997_haptic_pulse_mode mode; + + unsigned int internal_mode_pattern; + unsigned int pattern_cycle; + unsigned int pattern_signal_period; +}; + +static int max8997_haptic_set_duty_cycle(struct max8997_haptic *chip) +{ + int ret = 0; + + if (chip->mode == MAX8997_EXTERNAL_MODE) { + unsigned int duty = chip->pwm_period * chip->level / 100; + ret = pwm_config(chip->pwm, duty, chip->pwm_period); + } else { + int i; + u8 duty_index = 0; + + for (i = 0; i <= 64; i++) { + if (chip->level <= i * 100 / 64) { + duty_index = i; + break; + } + } + switch (chip->internal_mode_pattern) { + case 0: + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_SIGPWMDC1, duty_index); + break; + case 1: + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_SIGPWMDC2, duty_index); + break; + case 2: + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_SIGPWMDC3, duty_index); + break; + case 3: + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_SIGPWMDC4, duty_index); + break; + default: + break; + } + } + return ret; +} + +static void max8997_haptic_configure(struct max8997_haptic *chip) +{ + u8 value; + + value = chip->type << MAX8997_MOTOR_TYPE_SHIFT | + chip->enabled << MAX8997_ENABLE_SHIFT | + chip->mode << MAX8997_MODE_SHIFT | chip->pwm_divisor; + max8997_write_reg(chip->client, MAX8997_HAPTIC_REG_CONF2, value); + + if (chip->mode == MAX8997_INTERNAL_MODE && chip->enabled) { + value = chip->internal_mode_pattern << MAX8997_CYCLE_SHIFT | + chip->internal_mode_pattern << MAX8997_SIG_PERIOD_SHIFT | + chip->internal_mode_pattern << MAX8997_SIG_DUTY_SHIFT | + chip->internal_mode_pattern << MAX8997_PWM_DUTY_SHIFT; + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_DRVCONF, value); + + switch (chip->internal_mode_pattern) { + case 0: + value = chip->pattern_cycle << 4; + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_CYCLECONF1, value); + value = chip->pattern_signal_period; + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_SIGCONF1, value); + break; + + case 1: + value = chip->pattern_cycle; + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_CYCLECONF1, value); + value = chip->pattern_signal_period; + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_SIGCONF2, value); + break; + + case 2: + value = chip->pattern_cycle << 4; + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_CYCLECONF2, value); + value = chip->pattern_signal_period; + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_SIGCONF3, value); + break; + + case 3: + value = chip->pattern_cycle; + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_CYCLECONF2, value); + value = chip->pattern_signal_period; + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_SIGCONF4, value); + break; + + default: + break; + } + } +} + +static void max8997_haptic_enable(struct max8997_haptic *chip) +{ + int error; + + mutex_lock(&chip->mutex); + + error = max8997_haptic_set_duty_cycle(chip); + if (error) { + dev_err(chip->dev, "set_pwm_cycle failed, error: %d\n", error); + goto out; + } + + if (!chip->enabled) { + chip->enabled = true; + regulator_enable(chip->regulator); + max8997_haptic_configure(chip); + if (chip->mode == MAX8997_EXTERNAL_MODE) + pwm_enable(chip->pwm); + } + +out: + mutex_unlock(&chip->mutex); +} + +static void max8997_haptic_disable(struct max8997_haptic *chip) +{ + mutex_lock(&chip->mutex); + + if (chip->enabled) { + chip->enabled = false; + max8997_haptic_configure(chip); + if (chip->mode == MAX8997_EXTERNAL_MODE) + pwm_disable(chip->pwm); + regulator_disable(chip->regulator); + } + + mutex_unlock(&chip->mutex); +} + +static void max8997_haptic_play_effect_work(struct work_struct *work) +{ + struct max8997_haptic *chip = + container_of(work, struct max8997_haptic, work); + + if (chip->level) + max8997_haptic_enable(chip); + else + max8997_haptic_disable(chip); +} + +static int max8997_haptic_play_effect(struct input_dev *dev, void *data, + struct ff_effect *effect) +{ + struct max8997_haptic *chip = input_get_drvdata(dev); + + chip->level = effect->u.rumble.strong_magnitude; + if (!chip->level) + chip->level = effect->u.rumble.weak_magnitude; + + schedule_work(&chip->work); + + return 0; +} + +static void max8997_haptic_close(struct input_dev *dev) +{ + struct max8997_haptic *chip = input_get_drvdata(dev); + + cancel_work_sync(&chip->work); + max8997_haptic_disable(chip); +} + +static int __devinit max8997_haptic_probe(struct platform_device *pdev) +{ + struct max8997_dev *iodev = dev_get_drvdata(pdev->dev.parent); + const struct max8997_platform_data *pdata = + dev_get_platdata(iodev->dev); + const struct max8997_haptic_platform_data *haptic_pdata = + pdata->haptic_pdata; + struct max8997_haptic *chip; + struct input_dev *input_dev; + int error; + + if (!haptic_pdata) { + dev_err(&pdev->dev, "no haptic platform data\n"); + return -EINVAL; + } + + chip = kzalloc(sizeof(struct max8997_haptic), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!chip || !input_dev) { + dev_err(&pdev->dev, "unable to allocate memory\n"); + error = -ENOMEM; + goto err_free_mem; + } + + INIT_WORK(&chip->work, max8997_haptic_play_effect_work); + mutex_init(&chip->mutex); + + chip->client = iodev->haptic; + chip->dev = &pdev->dev; + chip->input_dev = input_dev; + chip->pwm_period = haptic_pdata->pwm_period; + chip->type = haptic_pdata->type; + chip->mode = haptic_pdata->mode; + chip->pwm_divisor = haptic_pdata->pwm_divisor; + + switch (chip->mode) { + case MAX8997_INTERNAL_MODE: + chip->internal_mode_pattern = + haptic_pdata->internal_mode_pattern; + chip->pattern_cycle = haptic_pdata->pattern_cycle; + chip->pattern_signal_period = + haptic_pdata->pattern_signal_period; + break; + + case MAX8997_EXTERNAL_MODE: + chip->pwm = pwm_request(haptic_pdata->pwm_channel_id, + "max8997-haptic"); + if (IS_ERR(chip->pwm)) { + error = PTR_ERR(chip->pwm); + dev_err(&pdev->dev, + "unable to request PWM for haptic, error: %d\n", + error); + goto err_free_mem; + } + break; + + default: + dev_err(&pdev->dev, + "Invalid chip mode specified (%d)\n", chip->mode); + error = -EINVAL; + goto err_free_mem; + } + + chip->regulator = regulator_get(&pdev->dev, "inmotor"); + if (IS_ERR(chip->regulator)) { + error = PTR_ERR(chip->regulator); + dev_err(&pdev->dev, + "unable to get regulator, error: %d\n", + error); + goto err_free_pwm; + } + + input_dev->name = "max8997-haptic"; + input_dev->id.version = 1; + input_dev->dev.parent = &pdev->dev; + input_dev->close = max8997_haptic_close; + input_set_drvdata(input_dev, chip); + input_set_capability(input_dev, EV_FF, FF_RUMBLE); + + error = input_ff_create_memless(input_dev, NULL, + max8997_haptic_play_effect); + if (error) { + dev_err(&pdev->dev, + "unable to create FF device, error: %d\n", + error); + goto err_put_regulator; + } + + error = input_register_device(input_dev); + if (error) { + dev_err(&pdev->dev, + "unable to register input device, error: %d\n", + error); + goto err_destroy_ff; + } + + platform_set_drvdata(pdev, chip); + return 0; + +err_destroy_ff: + input_ff_destroy(input_dev); +err_put_regulator: + regulator_put(chip->regulator); +err_free_pwm: + if (chip->mode == MAX8997_EXTERNAL_MODE) + pwm_free(chip->pwm); +err_free_mem: + input_free_device(input_dev); + kfree(chip); + + return error; +} + +static int __devexit max8997_haptic_remove(struct platform_device *pdev) +{ + struct max8997_haptic *chip = platform_get_drvdata(pdev); + + input_unregister_device(chip->input_dev); + regulator_put(chip->regulator); + + if (chip->mode == MAX8997_EXTERNAL_MODE) + pwm_free(chip->pwm); + + kfree(chip); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int max8997_haptic_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct max8997_haptic *chip = platform_get_drvdata(pdev); + + max8997_haptic_disable(chip); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(max8997_haptic_pm_ops, max8997_haptic_suspend, NULL); + +static const struct platform_device_id max8997_haptic_id[] = { + { "max8997-haptic", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, max8997_haptic_id); + +static struct platform_driver max8997_haptic_driver = { + .driver = { + .name = "max8997-haptic", + .owner = THIS_MODULE, + .pm = &max8997_haptic_pm_ops, + }, + .probe = max8997_haptic_probe, + .remove = __devexit_p(max8997_haptic_remove), + .id_table = max8997_haptic_id, +}; +module_platform_driver(max8997_haptic_driver); + +MODULE_ALIAS("platform:max8997-haptic"); +MODULE_AUTHOR("Donggeun Kim "); +MODULE_DESCRIPTION("max8997_haptic driver"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/mfd/max8997.h b/include/linux/mfd/max8997.h index fff590521e50..28726dd540f2 100644 --- a/include/linux/mfd/max8997.h +++ b/include/linux/mfd/max8997.h @@ -131,6 +131,55 @@ struct max8997_muic_platform_data { int num_init_data; }; +enum max8997_haptic_motor_type { + MAX8997_HAPTIC_ERM, + MAX8997_HAPTIC_LRA, +}; + +enum max8997_haptic_pulse_mode { + MAX8997_EXTERNAL_MODE, + MAX8997_INTERNAL_MODE, +}; + +enum max8997_haptic_pwm_divisor { + MAX8997_PWM_DIVISOR_32, + MAX8997_PWM_DIVISOR_64, + MAX8997_PWM_DIVISOR_128, + MAX8997_PWM_DIVISOR_256, +}; + +/** + * max8997_haptic_platform_data + * @pwm_channel_id: channel number of PWM device + * valid for MAX8997_EXTERNAL_MODE + * @pwm_period: period in nano second for PWM device + * valid for MAX8997_EXTERNAL_MODE + * @type: motor type + * @mode: pulse mode + * MAX8997_EXTERNAL_MODE: external PWM device is used to control motor + * MAX8997_INTERNAL_MODE: internal pulse generator is used to control motor + * @pwm_divisor: divisor for external PWM device + * @internal_mode_pattern: internal mode pattern for internal mode + * [0 - 3]: valid pattern number + * @pattern_cycle: the number of cycles of the waveform + * for the internal mode pattern + * [0 - 15]: available cycles + * @pattern_signal_period: period of the waveform for the internal mode pattern + * [0 - 255]: available period + */ +struct max8997_haptic_platform_data { + unsigned int pwm_channel_id; + unsigned int pwm_period; + + enum max8997_haptic_motor_type type; + enum max8997_haptic_pulse_mode mode; + enum max8997_haptic_pwm_divisor pwm_divisor; + + unsigned int internal_mode_pattern; + unsigned int pattern_cycle; + unsigned int pattern_signal_period; +}; + enum max8997_led_mode { MAX8997_NONE, MAX8997_FLASH_MODE, @@ -192,7 +241,9 @@ struct max8997_platform_data { /* ---- MUIC ---- */ struct max8997_muic_platform_data *muic_pdata; - /* HAPTIC: Not implemented */ + /* ---- HAPTIC ---- */ + struct max8997_haptic_platform_data *haptic_pdata; + /* RTC: Not implemented */ /* ---- LED ---- */ struct max8997_led_platform_data *led_pdata; -- cgit v1.2.3 From 043916608c4b309e98a1650520ed4e88ec5e9123 Mon Sep 17 00:00:00 2001 From: Christopher Hudson Date: Fri, 16 Mar 2012 22:47:47 -0700 Subject: Input: kxtj9 - who_am_i check value and initial data rate fixes Several fixes based on customer feedback: * WHO_AM_I value has changed since preliminary parts used for initial testing; * Output of le16_to_cpu must be saved to memory before shifting to preserve sign; * Initial data rate was not extracted from data control register init. This was causing the initial data rate to be set to maximum until it was changed. To fix this problem, it made more sense to specify initial data rate and extract the register mask from that. Signed-off-by: Chris Hudson Signed-off-by: Dmitry Torokhov --- drivers/input/misc/kxtj9.c | 22 +++++++++++++++++----- include/linux/input/kxtj9.h | 11 +---------- 2 files changed, 18 insertions(+), 15 deletions(-) (limited to 'include/linux') diff --git a/drivers/input/misc/kxtj9.c b/drivers/input/misc/kxtj9.c index 783597a9a64a..21edaba489be 100644 --- a/drivers/input/misc/kxtj9.c +++ b/drivers/input/misc/kxtj9.c @@ -41,6 +41,14 @@ #define PC1_ON (1 << 7) /* Data ready funtion enable bit: set during probe if using irq mode */ #define DRDYE (1 << 5) +/* DATA CONTROL REGISTER BITS */ +#define ODR12_5F 0 +#define ODR25F 1 +#define ODR50F 2 +#define ODR100F 3 +#define ODR200F 4 +#define ODR400F 5 +#define ODR800F 6 /* INTERRUPT CONTROL REGISTER 1 BITS */ /* Set these during probe if using irq mode */ #define KXTJ9_IEL (1 << 3) @@ -116,9 +124,13 @@ static void kxtj9_report_acceleration_data(struct kxtj9_data *tj9) if (err < 0) dev_err(&tj9->client->dev, "accelerometer data read failed\n"); - x = le16_to_cpu(acc_data[tj9->pdata.axis_map_x]) >> tj9->shift; - y = le16_to_cpu(acc_data[tj9->pdata.axis_map_y]) >> tj9->shift; - z = le16_to_cpu(acc_data[tj9->pdata.axis_map_z]) >> tj9->shift; + x = le16_to_cpu(acc_data[tj9->pdata.axis_map_x]); + y = le16_to_cpu(acc_data[tj9->pdata.axis_map_y]); + z = le16_to_cpu(acc_data[tj9->pdata.axis_map_z]); + + x >>= tj9->shift; + y >>= tj9->shift; + z >>= tj9->shift; input_report_abs(tj9->input_dev, ABS_X, tj9->pdata.negate_x ? -x : x); input_report_abs(tj9->input_dev, ABS_Y, tj9->pdata.negate_y ? -y : y); @@ -487,7 +499,7 @@ static int __devinit kxtj9_verify(struct kxtj9_data *tj9) goto out; } - retval = retval != 0x06 ? -EIO : 0; + retval = (retval != 0x07 && retval != 0x08) ? -EIO : 0; out: kxtj9_device_power_off(tj9); @@ -537,7 +549,7 @@ static int __devinit kxtj9_probe(struct i2c_client *client, i2c_set_clientdata(client, tj9); tj9->ctrl_reg1 = tj9->pdata.res_12bit | tj9->pdata.g_range; - tj9->data_ctrl = tj9->pdata.data_odr_init; + tj9->last_poll_interval = tj9->pdata.init_interval; if (client->irq) { /* If in irq mode, populate INT_CTRL_REG1 and enable DRDY. */ diff --git a/include/linux/input/kxtj9.h b/include/linux/input/kxtj9.h index f6bac89537b8..d415579b56fe 100644 --- a/include/linux/input/kxtj9.h +++ b/include/linux/input/kxtj9.h @@ -24,6 +24,7 @@ struct kxtj9_platform_data { unsigned int min_interval; /* minimum poll interval (in milli-seconds) */ + unsigned int init_interval; /* initial poll interval (in milli-seconds) */ /* * By default, x is axis 0, y is axis 1, z is axis 2; these can be @@ -52,16 +53,6 @@ struct kxtj9_platform_data { #define KXTJ9_G_8G (1 << 4) u8 g_range; - /* DATA_CTRL_REG: controls the output data rate of the part */ - #define ODR12_5F 0 - #define ODR25F 1 - #define ODR50F 2 - #define ODR100F 3 - #define ODR200F 4 - #define ODR400F 5 - #define ODR800F 6 - u8 data_odr_init; - int (*init)(void); void (*exit)(void); int (*power_on)(void); -- cgit v1.2.3 From 0f1142a514e101076bc01de2f93b242693d0f16f Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 16 Mar 2012 22:47:48 -0700 Subject: Input: omap4-keypad - move platform_data to This patch allows us to drop the OMAP dependency from the OMAP4 keypad driver. Signed-off-by: Felipe Balbi Signed-off-by: Sourav Poddar Signed-off-by: Dmitry Torokhov --- arch/arm/mach-omap2/board-4430sdp.c | 1 + arch/arm/mach-omap2/devices.c | 1 + arch/arm/plat-omap/include/plat/omap4-keypad.h | 9 --------- drivers/input/keyboard/Kconfig | 1 - drivers/input/keyboard/omap4-keypad.c | 2 +- include/linux/platform_data/omap4-keypad.h | 13 +++++++++++++ 6 files changed, 16 insertions(+), 11 deletions(-) create mode 100644 include/linux/platform_data/omap4-keypad.h (limited to 'include/linux') diff --git a/arch/arm/mach-omap2/board-4430sdp.c b/arch/arm/mach-omap2/board-4430sdp.c index 4e9071589bfb..d16902c9ee5a 100644 --- a/arch/arm/mach-omap2/board-4430sdp.c +++ b/arch/arm/mach-omap2/board-4430sdp.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include diff --git a/arch/arm/mach-omap2/devices.c b/arch/arm/mach-omap2/devices.c index 283d11eae693..58e1d201732e 100644 --- a/arch/arm/mach-omap2/devices.c +++ b/arch/arm/mach-omap2/devices.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include diff --git a/arch/arm/plat-omap/include/plat/omap4-keypad.h b/arch/arm/plat-omap/include/plat/omap4-keypad.h index 9fe6c8783236..8ad0a377a54b 100644 --- a/arch/arm/plat-omap/include/plat/omap4-keypad.h +++ b/arch/arm/plat-omap/include/plat/omap4-keypad.h @@ -1,15 +1,6 @@ #ifndef ARCH_ARM_PLAT_OMAP4_KEYPAD_H #define ARCH_ARM_PLAT_OMAP4_KEYPAD_H -#include - -struct omap4_keypad_platform_data { - const struct matrix_keymap_data *keymap_data; - - u8 rows; - u8 cols; -}; - extern int omap4_keyboard_init(struct omap4_keypad_platform_data *, struct omap_board_data *); #endif diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index 3371954c979b..f354813a13e8 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -513,7 +513,6 @@ config KEYBOARD_OMAP config KEYBOARD_OMAP4 tristate "TI OMAP4 keypad support" - depends on ARCH_OMAP4 help Say Y here if you want to use the OMAP4 keypad. diff --git a/drivers/input/keyboard/omap4-keypad.c b/drivers/input/keyboard/omap4-keypad.c index d5c5d77f4b82..e809ac095a38 100644 --- a/drivers/input/keyboard/omap4-keypad.c +++ b/drivers/input/keyboard/omap4-keypad.c @@ -31,7 +31,7 @@ #include #include -#include +#include /* OMAP4 registers */ #define OMAP4_KBD_REVISION 0x00 diff --git a/include/linux/platform_data/omap4-keypad.h b/include/linux/platform_data/omap4-keypad.h new file mode 100644 index 000000000000..4eef5fb05a17 --- /dev/null +++ b/include/linux/platform_data/omap4-keypad.h @@ -0,0 +1,13 @@ +#ifndef __LINUX_INPUT_OMAP4_KEYPAD_H +#define __LINUX_INPUT_OMAP4_KEYPAD_H + +#include + +struct omap4_keypad_platform_data { + const struct matrix_keymap_data *keymap_data; + + u8 rows; + u8 cols; +}; + +#endif /* __LINUX_INPUT_OMAP4_KEYPAD_H */ -- cgit v1.2.3 From a7611e84bd06c70c8b0cd88cd1e474c3c42fee3e Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Mon, 12 Mar 2012 21:46:55 +0100 Subject: firewire: cdev: fix IR multichannel event documentation State more precisely when fw_cdev_event_iso_interrupt_mc is sent. While the comment tried to reflect an amibuity in the OHCI specification, there is only one method that is useful in practice, and this also matches all the hardware implementations I've tested. Signed-off-by: Clemens Ladisch Signed-off-by: Stefan Richter --- include/linux/firewire-cdev.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/firewire-cdev.h b/include/linux/firewire-cdev.h index 357dbfc2829e..cc1b570a2eb3 100644 --- a/include/linux/firewire-cdev.h +++ b/include/linux/firewire-cdev.h @@ -267,9 +267,8 @@ struct fw_cdev_event_iso_interrupt { * * This event is sent in multichannel contexts (context type * %FW_CDEV_ISO_CONTEXT_RECEIVE_MULTICHANNEL) for &fw_cdev_iso_packet buffer - * chunks that have the %FW_CDEV_ISO_INTERRUPT bit set. Whether this happens - * when a packet is completed and/or when a buffer chunk is completed depends - * on the hardware implementation. + * chunks that have been completely filled and that have the + * %FW_CDEV_ISO_INTERRUPT bit set. * * The buffer is continuously filled with the following data, per packet: * - the 1394 iso packet header as described at &fw_cdev_event_iso_interrupt, -- cgit v1.2.3 From 8dd3775889345850ecddd689b5c200cdd91bd8c9 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Thu, 15 Mar 2012 17:16:40 -0400 Subject: NFSv4.1: Clean ups and bugfixes for the pNFS read/writeback/commit code Move more pnfs-isms out of the generic commit code. Bugfixes: - filelayout_scan_commit_lists doesn't need to get/put the lseg. In fact since it is run under the inode->i_lock, the lseg_put() can deadlock. - Ensure that we distinguish between what needs to be done for commit-to-data server and what needs to be done for commit-to-MDS using the new flag PG_COMMIT_TO_DS. Otherwise we may end up calling put_lseg() on a bucket for a struct nfs_page that got written through the MDS. - Fix a case where we were using list_del() on an nfs_page->wb_list instead of list_del_init(). - filelayout_initiate_commit needs to call filelayout_commit_release on error instead of the mds_ops->rpc_release(). Otherwise it won't clear the commit lock. Cleanups: - Let the files layout manage the commit lists for the pNFS case. Don't expose stuff like pnfs_choose_commit_list, and the fact that the commit buckets hold references to the layout segment in common code. - Cast out the put_lseg() calls for the struct nfs_read/write_data->lseg into the pNFS layer from whence they came. - Let the pNFS layer manage the NFS_INO_PNFS_COMMIT bit. Signed-off-by: Trond Myklebust Cc: Fred Isaman --- fs/nfs/internal.h | 4 +- fs/nfs/nfs4filelayout.c | 82 +++++++++++++++++++++++-------- fs/nfs/pnfs.c | 3 ++ fs/nfs/pnfs.h | 55 ++++++++++----------- fs/nfs/read.c | 1 - fs/nfs/write.c | 124 +++++++++++++++++++++++++++++------------------ include/linux/nfs_page.h | 11 +++++ 7 files changed, 183 insertions(+), 97 deletions(-) (limited to 'include/linux') diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index 04a914704e7b..2476dc69365f 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -308,8 +308,6 @@ extern void nfs_pageio_reset_read_mds(struct nfs_pageio_descriptor *pgio); extern void nfs_readdata_release(struct nfs_read_data *rdata); /* write.c */ -extern int nfs_scan_commit_list(struct list_head *src, struct list_head *dst, - int max); extern int nfs_generic_flush(struct nfs_pageio_descriptor *desc, struct list_head *head); extern void nfs_pageio_init_write_mds(struct nfs_pageio_descriptor *pgio, @@ -334,6 +332,8 @@ void nfs_retry_commit(struct list_head *page_list, void nfs_commit_clear_lock(struct nfs_inode *nfsi); void nfs_commitdata_release(void *data); void nfs_commit_release_pages(struct nfs_write_data *data); +void nfs_request_add_commit_list(struct nfs_page *req, struct list_head *head); +void nfs_request_remove_commit_list(struct nfs_page *req); #ifdef CONFIG_MIGRATION extern int nfs_migrate_page(struct address_space *, diff --git a/fs/nfs/nfs4filelayout.c b/fs/nfs/nfs4filelayout.c index 379a085f8f25..c24e077c2820 100644 --- a/fs/nfs/nfs4filelayout.c +++ b/fs/nfs/nfs4filelayout.c @@ -224,6 +224,7 @@ static void filelayout_read_release(void *data) { struct nfs_read_data *rdata = (struct nfs_read_data *)data; + put_lseg(rdata->lseg); rdata->mds_ops->rpc_release(data); } @@ -310,6 +311,7 @@ static void filelayout_write_release(void *data) { struct nfs_write_data *wdata = (struct nfs_write_data *)data; + put_lseg(wdata->lseg); wdata->mds_ops->rpc_release(data); } @@ -320,6 +322,7 @@ static void filelayout_commit_release(void *data) nfs_commit_release_pages(wdata); if (atomic_dec_and_test(&NFS_I(wdata->inode)->commits_outstanding)) nfs_commit_clear_lock(NFS_I(wdata->inode)); + put_lseg(wdata->lseg); nfs_commitdata_release(wdata); } @@ -779,11 +782,16 @@ static u32 select_bucket_index(struct nfs4_filelayout_segment *fl, u32 j) /* The generic layer is about to remove the req from the commit list. * If this will make the bucket empty, it will need to put the lseg reference. - * Note inode lock is held, so we can't do the put here. */ -static struct pnfs_layout_segment * -filelayout_remove_commit_req(struct nfs_page *req) +static void +filelayout_clear_request_commit(struct nfs_page *req) { + struct pnfs_layout_segment *freeme = NULL; + struct inode *inode = req->wb_context->dentry->d_inode; + + spin_lock(&inode->i_lock); + if (!test_and_clear_bit(PG_COMMIT_TO_DS, &req->wb_flags)) + goto out; if (list_is_singular(&req->wb_list)) { struct inode *inode = req->wb_context->dentry->d_inode; struct pnfs_layout_segment *lseg; @@ -792,11 +800,16 @@ filelayout_remove_commit_req(struct nfs_page *req) * since there is only one relevant lseg... */ list_for_each_entry(lseg, &NFS_I(inode)->layout->plh_segs, pls_list) { - if (lseg->pls_range.iomode == IOMODE_RW) - return lseg; + if (lseg->pls_range.iomode == IOMODE_RW) { + freeme = lseg; + break; + } } } - return NULL; +out: + nfs_request_remove_commit_list(req); + spin_unlock(&inode->i_lock); + put_lseg(freeme); } static struct list_head * @@ -829,9 +842,20 @@ filelayout_choose_commit_list(struct nfs_page *req, */ get_lseg(lseg); } + set_bit(PG_COMMIT_TO_DS, &req->wb_flags); return list; } +static void +filelayout_mark_request_commit(struct nfs_page *req, + struct pnfs_layout_segment *lseg) +{ + struct list_head *list; + + list = filelayout_choose_commit_list(req, lseg); + nfs_request_add_commit_list(req, list); +} + static u32 calc_ds_index_from_commit(struct pnfs_layout_segment *lseg, u32 i) { struct nfs4_filelayout_segment *flseg = FILELAYOUT_LSEG(lseg); @@ -872,7 +896,7 @@ static int filelayout_initiate_commit(struct nfs_write_data *data, int how) set_bit(lo_fail_bit(IOMODE_RW), &lseg->pls_layout->plh_flags); set_bit(lo_fail_bit(IOMODE_READ), &lseg->pls_layout->plh_flags); prepare_to_resend_writes(data); - data->mds_ops->rpc_release(data); + filelayout_commit_release(data); return -EAGAIN; } dprintk("%s ino %lu, how %d\n", __func__, data->inode->i_ino, how); @@ -895,7 +919,7 @@ find_only_write_lseg_locked(struct inode *inode) list_for_each_entry(lseg, &NFS_I(inode)->layout->plh_segs, pls_list) if (lseg->pls_range.iomode == IOMODE_RW) - return get_lseg(lseg); + return lseg; return NULL; } @@ -905,10 +929,33 @@ static struct pnfs_layout_segment *find_only_write_lseg(struct inode *inode) spin_lock(&inode->i_lock); rv = find_only_write_lseg_locked(inode); + if (rv) + get_lseg(rv); spin_unlock(&inode->i_lock); return rv; } +static int +filelayout_scan_ds_commit_list(struct nfs4_fl_commit_bucket *bucket, int max) +{ + struct list_head *src = &bucket->written; + struct list_head *dst = &bucket->committing; + struct nfs_page *req, *tmp; + int ret = 0; + + list_for_each_entry_safe(req, tmp, src, wb_list) { + if (!nfs_lock_request(req)) + continue; + nfs_request_remove_commit_list(req); + clear_bit(PG_COMMIT_TO_DS, &req->wb_flags); + nfs_list_add_request(req, dst); + ret++; + if (ret == max) + break; + } + return ret; +} + /* Move reqs from written to committing lists, returning count of number moved. * Note called with i_lock held. */ @@ -920,21 +967,16 @@ static int filelayout_scan_commit_lists(struct inode *inode, int max) lseg = find_only_write_lseg_locked(inode); if (!lseg) - return 0; + goto out_done; fl = FILELAYOUT_LSEG(lseg); if (fl->commit_through_mds) - goto out_put; - for (i = 0; i < fl->number_of_buckets; i++) { - if (list_empty(&fl->commit_buckets[i].written)) - continue; - cnt = nfs_scan_commit_list(&fl->commit_buckets[i].written, - &fl->commit_buckets[i].committing, - max); + goto out_done; + for (i = 0; i < fl->number_of_buckets && max != 0; i++) { + cnt = filelayout_scan_ds_commit_list(&fl->commit_buckets[i], max); max -= cnt; rv += cnt; } -out_put: - put_lseg(lseg); +out_done: return rv; } @@ -1033,8 +1075,8 @@ static struct pnfs_layoutdriver_type filelayout_type = { .free_lseg = filelayout_free_lseg, .pg_read_ops = &filelayout_pg_read_ops, .pg_write_ops = &filelayout_pg_write_ops, - .choose_commit_list = filelayout_choose_commit_list, - .remove_commit_req = filelayout_remove_commit_req, + .mark_request_commit = filelayout_mark_request_commit, + .clear_request_commit = filelayout_clear_request_commit, .scan_commit_lists = filelayout_scan_commit_lists, .commit_pagelist = filelayout_commit_pagelist, .read_pagelist = filelayout_read_pagelist, diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c index 6f1c1e3d12bc..b5d451586943 100644 --- a/fs/nfs/pnfs.c +++ b/fs/nfs/pnfs.c @@ -1210,6 +1210,7 @@ void pnfs_ld_write_done(struct nfs_write_data *data) } data->task.tk_status = pnfs_write_done_resend_to_mds(data->inode, &data->pages); } + put_lseg(data->lseg); data->mds_ops->rpc_release(data); } EXPORT_SYMBOL_GPL(pnfs_ld_write_done); @@ -1223,6 +1224,7 @@ pnfs_write_through_mds(struct nfs_pageio_descriptor *desc, nfs_list_add_request(data->req, &desc->pg_list); nfs_pageio_reset_write_mds(desc); desc->pg_recoalesce = 1; + put_lseg(data->lseg); nfs_writedata_release(data); } @@ -1323,6 +1325,7 @@ void pnfs_ld_read_done(struct nfs_read_data *data) data->mds_ops->rpc_call_done(&data->task, data); } else pnfs_ld_handle_read_error(data); + put_lseg(data->lseg); data->mds_ops->rpc_release(data); } EXPORT_SYMBOL_GPL(pnfs_ld_read_done); diff --git a/fs/nfs/pnfs.h b/fs/nfs/pnfs.h index ef92f676cf1e..e98ff3027d3a 100644 --- a/fs/nfs/pnfs.h +++ b/fs/nfs/pnfs.h @@ -94,9 +94,9 @@ struct pnfs_layoutdriver_type { const struct nfs_pageio_ops *pg_read_ops; const struct nfs_pageio_ops *pg_write_ops; - struct list_head * (*choose_commit_list) (struct nfs_page *req, + void (*mark_request_commit) (struct nfs_page *req, struct pnfs_layout_segment *lseg); - struct pnfs_layout_segment *(*remove_commit_req) (struct nfs_page *req); + void (*clear_request_commit) (struct nfs_page *req); int (*scan_commit_lists) (struct inode *inode, int max); int (*commit_pagelist)(struct inode *inode, struct list_head *mds_pages, int how); @@ -269,39 +269,42 @@ pnfs_commit_list(struct inode *inode, struct list_head *mds_pages, int how) return NFS_SERVER(inode)->pnfs_curr_ld->commit_pagelist(inode, mds_pages, how); } -static inline struct list_head * -pnfs_choose_commit_list(struct nfs_page *req, struct pnfs_layout_segment *lseg) +static inline bool +pnfs_mark_request_commit(struct nfs_page *req, struct pnfs_layout_segment *lseg) { struct inode *inode = req->wb_context->dentry->d_inode; - struct list_head *rv; + struct pnfs_layoutdriver_type *ld = NFS_SERVER(inode)->pnfs_curr_ld; - if (lseg && NFS_SERVER(inode)->pnfs_curr_ld->choose_commit_list) - rv = NFS_SERVER(inode)->pnfs_curr_ld->choose_commit_list(req, lseg); - else - rv = &NFS_I(inode)->commit_list; - return rv; + if (lseg == NULL || ld->mark_request_commit == NULL) + return false; + ld->mark_request_commit(req, lseg); + return true; } -static inline struct pnfs_layout_segment * +static inline bool pnfs_clear_request_commit(struct nfs_page *req) { struct inode *inode = req->wb_context->dentry->d_inode; + struct pnfs_layoutdriver_type *ld = NFS_SERVER(inode)->pnfs_curr_ld; - if (NFS_SERVER(inode)->pnfs_curr_ld && - NFS_SERVER(inode)->pnfs_curr_ld->remove_commit_req) - return NFS_SERVER(inode)->pnfs_curr_ld->remove_commit_req(req); - else - return NULL; + if (ld == NULL || ld->clear_request_commit == NULL) + return false; + ld->clear_request_commit(req); + return true; } static inline int pnfs_scan_commit_lists(struct inode *inode, int max) { - if (NFS_SERVER(inode)->pnfs_curr_ld && - NFS_SERVER(inode)->pnfs_curr_ld->scan_commit_lists) - return NFS_SERVER(inode)->pnfs_curr_ld->scan_commit_lists(inode, max); - else + struct pnfs_layoutdriver_type *ld = NFS_SERVER(inode)->pnfs_curr_ld; + int ret; + + if (ld == NULL || ld->scan_commit_lists == NULL) return 0; + ret = ld->scan_commit_lists(inode, max); + if (ret != 0) + set_bit(NFS_INO_PNFS_COMMIT, &NFS_I(inode)->flags); + return ret; } /* Should the pNFS client commit and return the layout upon a setattr */ @@ -403,18 +406,16 @@ pnfs_commit_list(struct inode *inode, struct list_head *mds_pages, int how) return PNFS_NOT_ATTEMPTED; } -static inline struct list_head * -pnfs_choose_commit_list(struct nfs_page *req, struct pnfs_layout_segment *lseg) +static inline bool +pnfs_mark_request_commit(struct nfs_page *req, struct pnfs_layout_segment *lseg) { - struct inode *inode = req->wb_context->dentry->d_inode; - - return &NFS_I(inode)->commit_list; + return false; } -static inline struct pnfs_layout_segment * +static inline bool pnfs_clear_request_commit(struct nfs_page *req) { - return NULL; + return false; } static inline int diff --git a/fs/nfs/read.c b/fs/nfs/read.c index 3c2540d532c7..2662c0298dd0 100644 --- a/fs/nfs/read.c +++ b/fs/nfs/read.c @@ -66,7 +66,6 @@ void nfs_readdata_free(struct nfs_read_data *p) void nfs_readdata_release(struct nfs_read_data *rdata) { - put_lseg(rdata->lseg); put_nfs_open_context(rdata->args.context); nfs_readdata_free(rdata); } diff --git a/fs/nfs/write.c b/fs/nfs/write.c index a630ad65d64c..0de19f413f92 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -100,7 +100,6 @@ void nfs_writedata_free(struct nfs_write_data *p) void nfs_writedata_release(struct nfs_write_data *wdata) { - put_lseg(wdata->lseg); put_nfs_open_context(wdata->args.context); nfs_writedata_free(wdata); } @@ -393,8 +392,6 @@ static void nfs_inode_add_request(struct inode *inode, struct nfs_page *req) spin_unlock(&inode->i_lock); } -static struct pnfs_layout_segment *nfs_clear_request_commit(struct nfs_page *req); - /* * Remove a write request from an inode */ @@ -402,18 +399,15 @@ static void nfs_inode_remove_request(struct nfs_page *req) { struct inode *inode = req->wb_context->dentry->d_inode; struct nfs_inode *nfsi = NFS_I(inode); - struct pnfs_layout_segment *lseg; BUG_ON (!NFS_WBACK_BUSY(req)); spin_lock(&inode->i_lock); - lseg = nfs_clear_request_commit(req); set_page_private(req->wb_page, 0); ClearPagePrivate(req->wb_page); clear_bit(PG_MAPPED, &req->wb_flags); nfsi->npages--; spin_unlock(&inode->i_lock); - put_lseg(lseg); nfs_release_request(req); } @@ -424,26 +418,69 @@ nfs_mark_request_dirty(struct nfs_page *req) } #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) -/* - * Add a request to the inode's commit list. +/** + * nfs_request_add_commit_list - add request to a commit list + * @req: pointer to a struct nfs_page + * @head: commit list head + * + * This sets the PG_CLEAN bit, updates the inode global count of + * number of outstanding requests requiring a commit as well as + * the MM page stats. + * + * The caller must _not_ hold the inode->i_lock, but must be + * holding the nfs_page lock. */ -static void -nfs_mark_request_commit(struct nfs_page *req, struct pnfs_layout_segment *lseg) +void +nfs_request_add_commit_list(struct nfs_page *req, struct list_head *head) { struct inode *inode = req->wb_context->dentry->d_inode; - struct nfs_inode *nfsi = NFS_I(inode); - struct list_head *clist; - clist = pnfs_choose_commit_list(req, lseg); - spin_lock(&inode->i_lock); set_bit(PG_CLEAN, &(req)->wb_flags); - nfs_list_add_request(req, clist); - nfsi->ncommit++; + spin_lock(&inode->i_lock); + nfs_list_add_request(req, head); + NFS_I(inode)->ncommit++; spin_unlock(&inode->i_lock); inc_zone_page_state(req->wb_page, NR_UNSTABLE_NFS); inc_bdi_stat(req->wb_page->mapping->backing_dev_info, BDI_RECLAIMABLE); __mark_inode_dirty(inode, I_DIRTY_DATASYNC); } +EXPORT_SYMBOL_GPL(nfs_request_add_commit_list); + +/** + * nfs_request_remove_commit_list - Remove request from a commit list + * @req: pointer to a nfs_page + * + * This clears the PG_CLEAN bit, and updates the inode global count of + * number of outstanding requests requiring a commit + * It does not update the MM page stats. + * + * The caller _must_ hold the inode->i_lock and the nfs_page lock. + */ +void +nfs_request_remove_commit_list(struct nfs_page *req) +{ + struct inode *inode = req->wb_context->dentry->d_inode; + + if (!test_and_clear_bit(PG_CLEAN, &(req)->wb_flags)) + return; + nfs_list_remove_request(req); + NFS_I(inode)->ncommit--; +} +EXPORT_SYMBOL_GPL(nfs_request_remove_commit_list); + + +/* + * Add a request to the inode's commit list. + */ +static void +nfs_mark_request_commit(struct nfs_page *req, struct pnfs_layout_segment *lseg) +{ + struct inode *inode = req->wb_context->dentry->d_inode; + + if (pnfs_mark_request_commit(req, lseg)) + return; + nfs_request_add_commit_list(req, &NFS_I(inode)->commit_list); +} static void nfs_clear_page_commit(struct page *page) @@ -452,18 +489,19 @@ nfs_clear_page_commit(struct page *page) dec_bdi_stat(page->mapping->backing_dev_info, BDI_RECLAIMABLE); } -static struct pnfs_layout_segment * +static void nfs_clear_request_commit(struct nfs_page *req) { - struct pnfs_layout_segment *lseg = NULL; + if (test_bit(PG_CLEAN, &req->wb_flags)) { + struct inode *inode = req->wb_context->dentry->d_inode; - if (test_and_clear_bit(PG_CLEAN, &(req)->wb_flags)) { + if (!pnfs_clear_request_commit(req)) { + spin_lock(&inode->i_lock); + nfs_request_remove_commit_list(req); + spin_unlock(&inode->i_lock); + } nfs_clear_page_commit(req->wb_page); - lseg = pnfs_clear_request_commit(req); - NFS_I(req->wb_context->dentry->d_inode)->ncommit--; - list_del(&req->wb_list); } - return lseg; } static inline @@ -490,15 +528,14 @@ int nfs_reschedule_unstable_write(struct nfs_page *req, return 0; } #else -static inline void +static void nfs_mark_request_commit(struct nfs_page *req, struct pnfs_layout_segment *lseg) { } -static inline struct pnfs_layout_segment * +static void nfs_clear_request_commit(struct nfs_page *req) { - return NULL; } static inline @@ -523,25 +560,23 @@ nfs_need_commit(struct nfs_inode *nfsi) } /* i_lock held by caller */ -int +static int nfs_scan_commit_list(struct list_head *src, struct list_head *dst, int max) { struct nfs_page *req, *tmp; int ret = 0; list_for_each_entry_safe(req, tmp, src, wb_list) { - if (nfs_lock_request_dontget(req)) { - kref_get(&req->wb_kref); - list_move_tail(&req->wb_list, dst); - clear_bit(PG_CLEAN, &(req)->wb_flags); - ret++; - if (ret == max) - break; - } + if (!nfs_lock_request(req)) + continue; + nfs_request_remove_commit_list(req); + nfs_list_add_request(req, dst); + ret++; + if (ret == max) + break; } return ret; } -EXPORT_SYMBOL_GPL(nfs_scan_commit_list); /* * nfs_scan_commit - Scan an inode for commit requests @@ -559,14 +594,12 @@ nfs_scan_commit(struct inode *inode, struct list_head *dst) spin_lock(&inode->i_lock); if (nfsi->ncommit > 0) { + const int max = INT_MAX; int pnfs_ret; - ret = nfs_scan_commit_list(&nfsi->commit_list, dst, INT_MAX); - pnfs_ret = pnfs_scan_commit_lists(inode, INT_MAX - ret); - if (pnfs_ret) { - ret += pnfs_ret; - set_bit(NFS_INO_PNFS_COMMIT, &nfsi->flags); - } + ret = nfs_scan_commit_list(&nfsi->commit_list, dst, max); + pnfs_ret = pnfs_scan_commit_lists(inode, max - ret); + ret += pnfs_ret; nfsi->ncommit -= ret; } spin_unlock(&inode->i_lock); @@ -601,7 +634,6 @@ static struct nfs_page *nfs_try_to_update_request(struct inode *inode, unsigned int rqend; unsigned int end; int error; - struct pnfs_layout_segment *lseg = NULL; if (!PagePrivate(page)) return NULL; @@ -637,8 +669,6 @@ static struct nfs_page *nfs_try_to_update_request(struct inode *inode, spin_lock(&inode->i_lock); } - lseg = nfs_clear_request_commit(req); - /* Okay, the request matches. Update the region */ if (offset < req->wb_offset) { req->wb_offset = offset; @@ -650,7 +680,7 @@ static struct nfs_page *nfs_try_to_update_request(struct inode *inode, req->wb_bytes = rqend - req->wb_offset; out_unlock: spin_unlock(&inode->i_lock); - put_lseg(lseg); + nfs_clear_request_commit(req); return req; out_flushme: spin_unlock(&inode->i_lock); @@ -1337,7 +1367,6 @@ void nfs_commitdata_release(void *data) { struct nfs_write_data *wdata = data; - put_lseg(wdata->lseg); put_nfs_open_context(wdata->args.context); nfs_commit_free(wdata); } @@ -1647,6 +1676,7 @@ int nfs_wb_page_cancel(struct inode *inode, struct page *page) if (req == NULL) break; if (nfs_lock_request_dontget(req)) { + nfs_clear_request_commit(req); nfs_inode_remove_request(req); /* * In case nfs_inode_remove_request has marked the diff --git a/include/linux/nfs_page.h b/include/linux/nfs_page.h index 50856e9c1e5f..eac30d6bec17 100644 --- a/include/linux/nfs_page.h +++ b/include/linux/nfs_page.h @@ -28,6 +28,7 @@ enum { PG_NEED_COMMIT, PG_NEED_RESCHED, PG_PARTIAL_READ_FAILED, + PG_COMMIT_TO_DS, }; struct nfs_inode; @@ -104,6 +105,16 @@ nfs_lock_request_dontget(struct nfs_page *req) return !test_and_set_bit(PG_BUSY, &req->wb_flags); } +static inline int +nfs_lock_request(struct nfs_page *req) +{ + if (test_and_set_bit(PG_BUSY, &req->wb_flags)) + return 0; + kref_get(&req->wb_kref); + return 1; +} + + /** * nfs_list_add_request - Insert a request into a list * @req: request -- cgit v1.2.3 From 5c6a7a62c130afef3d61c1dee153012231ff5cd9 Mon Sep 17 00:00:00 2001 From: Olivier Sobrie Date: Fri, 16 Mar 2012 23:57:09 -0700 Subject: Input: ili210x - add support for Ilitek ILI210x based touchscreens The driver supports chipsets ILI2102, ILI2102s, ILI2103, ILI2103s and ILI2105. Such kind of controllers can be found in Amazon Kindle Fire devices. Reviewed-by: Jan Paesmans Reviewed-by: Henrik Rydberg Signed-off-by: Olivier Sobrie Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/Kconfig | 15 ++ drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/ili210x.c | 360 ++++++++++++++++++++++++++++++++++++ include/linux/input/ili210x.h | 10 + 4 files changed, 386 insertions(+) create mode 100644 drivers/input/touchscreen/ili210x.c create mode 100644 include/linux/input/ili210x.h (limited to 'include/linux') diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index fc087b3c95cf..97b31a0e0525 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -243,6 +243,21 @@ config TOUCHSCREEN_FUJITSU To compile this driver as a module, choose M here: the module will be called fujitsu-ts. +config TOUCHSCREEN_ILI210X + tristate "Ilitek ILI210X based touchscreen" + depends on I2C + help + Say Y here if you have a ILI210X based touchscreen + controller. This driver supports models ILI2102, + ILI2102s, ILI2103, ILI2103s and ILI2105. + Such kind of chipsets can be found in Amazon Kindle Fire + touchscreens. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called ili210x. + config TOUCHSCREEN_S3C2410 tristate "Samsung S3C2410/generic touchscreen input driver" depends on ARCH_S3C2410 || SAMSUNG_DEV_TS diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index e748fb837759..3d5cf8cbf89c 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -29,6 +29,7 @@ obj-$(CONFIG_TOUCHSCREEN_EETI) += eeti_ts.o obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o obj-$(CONFIG_TOUCHSCREEN_EGALAX) += egalax_ts.o obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o +obj-$(CONFIG_TOUCHSCREEN_ILI210X) += ili210x.o obj-$(CONFIG_TOUCHSCREEN_INEXIO) += inexio.o obj-$(CONFIG_TOUCHSCREEN_INTEL_MID) += intel-mid-touch.o obj-$(CONFIG_TOUCHSCREEN_LPC32XX) += lpc32xx_ts.o diff --git a/drivers/input/touchscreen/ili210x.c b/drivers/input/touchscreen/ili210x.c new file mode 100644 index 000000000000..c0044175a921 --- /dev/null +++ b/drivers/input/touchscreen/ili210x.c @@ -0,0 +1,360 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_TOUCHES 2 +#define DEFAULT_POLL_PERIOD 20 + +/* Touchscreen commands */ +#define REG_TOUCHDATA 0x10 +#define REG_PANEL_INFO 0x20 +#define REG_FIRMWARE_VERSION 0x40 +#define REG_CALIBRATE 0xcc + +struct finger { + u8 x_low; + u8 x_high; + u8 y_low; + u8 y_high; +} __packed; + +struct touchdata { + u8 status; + struct finger finger[MAX_TOUCHES]; +} __packed; + +struct panel_info { + struct finger finger_max; + u8 xchannel_num; + u8 ychannel_num; +} __packed; + +struct firmware_version { + u8 id; + u8 major; + u8 minor; +} __packed; + +struct ili210x { + struct i2c_client *client; + struct input_dev *input; + bool (*get_pendown_state)(void); + unsigned int poll_period; + struct delayed_work dwork; +}; + +static int ili210x_read_reg(struct i2c_client *client, u8 reg, void *buf, + size_t len) +{ + struct i2c_msg msg[2] = { + { + .addr = client->addr, + .flags = 0, + .len = 1, + .buf = ®, + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = len, + .buf = buf, + } + }; + + if (i2c_transfer(client->adapter, msg, 2) != 2) { + dev_err(&client->dev, "i2c transfer failed\n"); + return -EIO; + } + + return 0; +} + +static void ili210x_report_events(struct input_dev *input, + const struct touchdata *touchdata) +{ + int i; + bool touch; + unsigned int x, y; + const struct finger *finger; + + for (i = 0; i < MAX_TOUCHES; i++) { + input_mt_slot(input, i); + + finger = &touchdata->finger[i]; + + touch = touchdata->status & (1 << i); + input_mt_report_slot_state(input, MT_TOOL_FINGER, touch); + if (touch) { + x = finger->x_low | (finger->x_high << 8); + y = finger->y_low | (finger->y_high << 8); + + input_report_abs(input, ABS_MT_POSITION_X, x); + input_report_abs(input, ABS_MT_POSITION_Y, y); + } + } + + input_mt_report_pointer_emulation(input, false); + input_sync(input); +} + +static bool get_pendown_state(const struct ili210x *priv) +{ + bool state = false; + + if (priv->get_pendown_state) + state = priv->get_pendown_state(); + + return state; +} + +static void ili210x_work(struct work_struct *work) +{ + struct ili210x *priv = container_of(work, struct ili210x, + dwork.work); + struct i2c_client *client = priv->client; + struct touchdata touchdata; + int error; + + error = ili210x_read_reg(client, REG_TOUCHDATA, + &touchdata, sizeof(touchdata)); + if (error) { + dev_err(&client->dev, + "Unable to get touchdata, err = %d\n", error); + return; + } + + ili210x_report_events(priv->input, &touchdata); + + if ((touchdata.status & 0xf3) || get_pendown_state(priv)) + schedule_delayed_work(&priv->dwork, + msecs_to_jiffies(priv->poll_period)); +} + +static irqreturn_t ili210x_irq(int irq, void *irq_data) +{ + struct ili210x *priv = irq_data; + + schedule_delayed_work(&priv->dwork, 0); + + return IRQ_HANDLED; +} + +static ssize_t ili210x_calibrate(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ili210x *priv = i2c_get_clientdata(client); + unsigned long calibrate; + int rc; + u8 cmd = REG_CALIBRATE; + + if (kstrtoul(buf, 10, &calibrate)) + return -EINVAL; + + if (calibrate > 1) + return -EINVAL; + + if (calibrate) { + rc = i2c_master_send(priv->client, &cmd, sizeof(cmd)); + if (rc != sizeof(cmd)) + return -EIO; + } + + return count; +} +static DEVICE_ATTR(calibrate, 0644, NULL, ili210x_calibrate); + +static struct attribute *ili210x_attributes[] = { + &dev_attr_calibrate.attr, + NULL, +}; + +static const struct attribute_group ili210x_attr_group = { + .attrs = ili210x_attributes, +}; + +static int __devinit ili210x_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + const struct ili210x_platform_data *pdata = dev->platform_data; + struct ili210x *priv; + struct input_dev *input; + struct panel_info panel; + struct firmware_version firmware; + int xmax, ymax; + int error; + + dev_dbg(dev, "Probing for ILI210X I2C Touschreen driver"); + + if (!pdata) { + dev_err(dev, "No platform data!\n"); + return -EINVAL; + } + + if (client->irq <= 0) { + dev_err(dev, "No IRQ!\n"); + return -EINVAL; + } + + /* Get firmware version */ + error = ili210x_read_reg(client, REG_FIRMWARE_VERSION, + &firmware, sizeof(firmware)); + if (error) { + dev_err(dev, "Failed to get firmware version, err: %d\n", + error); + return error; + } + + /* get panel info */ + error = ili210x_read_reg(client, REG_PANEL_INFO, &panel, sizeof(panel)); + if (error) { + dev_err(dev, "Failed to get panel informations, err: %d\n", + error); + return error; + } + + xmax = panel.finger_max.x_low | (panel.finger_max.x_high << 8); + ymax = panel.finger_max.y_low | (panel.finger_max.y_high << 8); + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + input = input_allocate_device(); + if (!priv || !input) { + error = -ENOMEM; + goto err_free_mem; + } + + priv->client = client; + priv->input = input; + priv->get_pendown_state = pdata->get_pendown_state; + priv->poll_period = pdata->poll_period ? : DEFAULT_POLL_PERIOD; + INIT_DELAYED_WORK(&priv->dwork, ili210x_work); + + /* Setup input device */ + input->name = "ILI210x Touchscreen"; + input->id.bustype = BUS_I2C; + input->dev.parent = dev; + + __set_bit(EV_SYN, input->evbit); + __set_bit(EV_KEY, input->evbit); + __set_bit(EV_ABS, input->evbit); + __set_bit(BTN_TOUCH, input->keybit); + + /* Single touch */ + input_set_abs_params(input, ABS_X, 0, xmax, 0, 0); + input_set_abs_params(input, ABS_Y, 0, ymax, 0, 0); + + /* Multi touch */ + input_mt_init_slots(input, MAX_TOUCHES); + input_set_abs_params(input, ABS_MT_POSITION_X, 0, xmax, 0, 0); + input_set_abs_params(input, ABS_MT_POSITION_Y, 0, ymax, 0, 0); + + input_set_drvdata(input, priv); + i2c_set_clientdata(client, priv); + + error = request_irq(client->irq, ili210x_irq, pdata->irq_flags, + client->name, priv); + if (error) { + dev_err(dev, "Unable to request touchscreen IRQ, err: %d\n", + error); + goto err_free_mem; + } + + error = sysfs_create_group(&dev->kobj, &ili210x_attr_group); + if (error) { + dev_err(dev, "Unable to create sysfs attributes, err: %d\n", + error); + goto err_free_irq; + } + + error = input_register_device(priv->input); + if (error) { + dev_err(dev, "Cannot regiser input device, err: %d\n", error); + goto err_remove_sysfs; + } + + device_init_wakeup(&client->dev, 1); + + dev_dbg(dev, + "ILI210x initialized (IRQ: %d), firmware version %d.%d.%d", + client->irq, firmware.id, firmware.major, firmware.minor); + + return 0; + +err_remove_sysfs: + sysfs_remove_group(&dev->kobj, &ili210x_attr_group); +err_free_irq: + free_irq(client->irq, priv); +err_free_mem: + input_free_device(input); + kfree(priv); + return error; +} + +static int __devexit ili210x_i2c_remove(struct i2c_client *client) +{ + struct ili210x *priv = i2c_get_clientdata(client); + + sysfs_remove_group(&client->dev.kobj, &ili210x_attr_group); + free_irq(priv->client->irq, priv); + cancel_delayed_work_sync(&priv->dwork); + input_unregister_device(priv->input); + kfree(priv); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int ili210x_i2c_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + + if (device_may_wakeup(&client->dev)) + enable_irq_wake(client->irq); + + return 0; +} + +static int ili210x_i2c_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + + if (device_may_wakeup(&client->dev)) + disable_irq_wake(client->irq); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(ili210x_i2c_pm, + ili210x_i2c_suspend, ili210x_i2c_resume); + +static const struct i2c_device_id ili210x_i2c_id[] = { + { "ili210x", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ili210x_i2c_id); + +static struct i2c_driver ili210x_ts_driver = { + .driver = { + .name = "ili210x_i2c", + .owner = THIS_MODULE, + .pm = &ili210x_i2c_pm, + }, + .id_table = ili210x_i2c_id, + .probe = ili210x_i2c_probe, + .remove = __devexit_p(ili210x_i2c_remove), +}; + +module_i2c_driver(ili210x_ts_driver); + +MODULE_AUTHOR("Olivier Sobrie "); +MODULE_DESCRIPTION("ILI210X I2C Touchscreen Driver"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/input/ili210x.h b/include/linux/input/ili210x.h new file mode 100644 index 000000000000..a5471245a13c --- /dev/null +++ b/include/linux/input/ili210x.h @@ -0,0 +1,10 @@ +#ifndef _ILI210X_H +#define _ILI210X_H + +struct ili210x_platform_data { + unsigned long irq_flags; + unsigned int poll_period; + bool (*get_pendown_state)(void); +}; + +#endif -- cgit v1.2.3 From 18d627113b830cda80792e96b28341bcd41cf40c Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Sun, 18 Mar 2012 19:05:29 +0100 Subject: firewire: prevent dropping of completed iso packet header data The buffer for the header data of completed iso packets has a fixed size, so it is possible to configure a stream with a big interval between interrupt packets or with big headers so that this buffer would overflow. Previously, ohci.c would drop any data that would not fit, but this could make unsuspecting applications believe that fewer than the actual number of packets have completed. Instead of dropping data, add calls to flush_iso_completion() so that there are as many events as needed to report all of the data. Signed-off-by: Clemens Ladisch Signed-off-by: Stefan Richter --- drivers/firewire/core-cdev.c | 2 +- drivers/firewire/ohci.c | 18 +++++++++--------- include/linux/firewire-cdev.h | 9 +++++++-- 3 files changed, 17 insertions(+), 12 deletions(-) (limited to 'include/linux') diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index f5f5a9706fcc..4cb27dc542ce 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -51,7 +51,7 @@ /* * ABI version history is documented in linux/firewire-cdev.h. */ -#define FW_CDEV_KERNEL_VERSION 4 +#define FW_CDEV_KERNEL_VERSION 5 #define FW_CDEV_VERSION_EVENT_REQUEST2 4 #define FW_CDEV_VERSION_ALLOCATE_REGION_END 4 diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index 07d3c8d58b30..632562667a01 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -2689,7 +2689,7 @@ static void copy_iso_headers(struct iso_context *ctx, const u32 *dma_hdr) u32 *ctx_hdr; if (ctx->header_length + ctx->base.header_size > PAGE_SIZE) - return; + flush_iso_completions(ctx); ctx_hdr = ctx->header + ctx->header_length; ctx->last_timestamp = (u16)le32_to_cpu((__force __le32)dma_hdr[0]); @@ -2826,16 +2826,16 @@ static int handle_it_packet(struct context *context, sync_it_packet_for_cpu(context, d); - if (ctx->header_length + 4 < PAGE_SIZE) { - ctx_hdr = ctx->header + ctx->header_length; - /* Present this value as big-endian to match the receive code */ - *ctx_hdr = cpu_to_be32( - ((u32)le16_to_cpu(pd->transfer_status) << 16) | - le16_to_cpu(pd->res_count)); - ctx->header_length += 4; - } + if (ctx->header_length + 4 > PAGE_SIZE) + flush_iso_completions(ctx); + ctx_hdr = ctx->header + ctx->header_length; ctx->last_timestamp = le16_to_cpu(last->res_count); + /* Present this value as big-endian to match the receive code */ + *ctx_hdr = cpu_to_be32((le16_to_cpu(pd->transfer_status) << 16) | + le16_to_cpu(pd->res_count)); + ctx->header_length += 4; + if (last->control & cpu_to_le16(DESCRIPTOR_IRQ_ALWAYS)) flush_iso_completions(ctx); diff --git a/include/linux/firewire-cdev.h b/include/linux/firewire-cdev.h index cc1b570a2eb3..b9bd349c6930 100644 --- a/include/linux/firewire-cdev.h +++ b/include/linux/firewire-cdev.h @@ -207,12 +207,15 @@ struct fw_cdev_event_request2 { * @closure: See &fw_cdev_event_common; * set by %FW_CDEV_CREATE_ISO_CONTEXT ioctl * @type: See &fw_cdev_event_common; always %FW_CDEV_EVENT_ISO_INTERRUPT - * @cycle: Cycle counter of the interrupt packet + * @cycle: Cycle counter of the last completed packet * @header_length: Total length of following headers, in bytes * @header: Stripped headers, if any * * This event is sent when the controller has completed an &fw_cdev_iso_packet - * with the %FW_CDEV_ISO_INTERRUPT bit set. + * with the %FW_CDEV_ISO_INTERRUPT bit set, or when there have been so many + * completed packets without the interrupt bit set that the kernel's internal + * buffer for @header is about to overflow. (In the latter case, kernels with + * ABI version < 5 drop header data up to the next interrupt packet.) * * Isochronous transmit events (context type %FW_CDEV_ISO_CONTEXT_TRANSMIT): * @@ -440,6 +443,8 @@ union fw_cdev_event { * - added %FW_CDEV_EVENT_ISO_INTERRUPT_MULTICHANNEL, * %FW_CDEV_ISO_CONTEXT_RECEIVE_MULTICHANNEL, and * %FW_CDEV_IOC_SET_ISO_CHANNELS + * 5 (3.4) - send %FW_CDEV_EVENT_ISO_INTERRUPT events when needed to + * avoid dropping data */ /** -- cgit v1.2.3 From d1bbd20972936b9b178fda3eb1ec417cb27fdc01 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Sun, 18 Mar 2012 19:06:39 +0100 Subject: firewire: allow explicit flushing of iso packet completions Extend the kernel and userspace APIs to allow reporting all currently completed isochronous packets, even if the next interrupt packet has not yet been reached. This is required to determine the status of the packets at the end of a paused or stopped stream, and useful for more precise synchronization of audio streams. Signed-off-by: Clemens Ladisch Signed-off-by: Stefan Richter --- drivers/firewire/core-card.c | 6 ++++ drivers/firewire/core-cdev.c | 12 +++++++ drivers/firewire/core-iso.c | 6 ++++ drivers/firewire/core.h | 2 ++ drivers/firewire/ohci.c | 78 ++++++++++++++++++++++++++++++++++++++----- include/linux/firewire-cdev.h | 35 ++++++++++++++++--- include/linux/firewire.h | 1 + 7 files changed, 127 insertions(+), 13 deletions(-) (limited to 'include/linux') diff --git a/drivers/firewire/core-card.c b/drivers/firewire/core-card.c index b19db0f6a254..cc595eba7ba9 100644 --- a/drivers/firewire/core-card.c +++ b/drivers/firewire/core-card.c @@ -650,6 +650,11 @@ static void dummy_flush_queue_iso(struct fw_iso_context *ctx) { } +static int dummy_flush_iso_completions(struct fw_iso_context *ctx) +{ + return -ENODEV; +} + static const struct fw_card_driver dummy_driver_template = { .read_phy_reg = dummy_read_phy_reg, .update_phy_reg = dummy_update_phy_reg, @@ -662,6 +667,7 @@ static const struct fw_card_driver dummy_driver_template = { .set_iso_channels = dummy_set_iso_channels, .queue_iso = dummy_queue_iso, .flush_queue_iso = dummy_flush_queue_iso, + .flush_iso_completions = dummy_flush_iso_completions, }; void fw_card_release(struct kref *kref) diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index 4cb27dc542ce..22c6df5f136d 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -438,6 +438,7 @@ union ioctl_arg { struct fw_cdev_send_phy_packet send_phy_packet; struct fw_cdev_receive_phy_packets receive_phy_packets; struct fw_cdev_set_iso_channels set_iso_channels; + struct fw_cdev_flush_iso flush_iso; }; static int ioctl_get_info(struct client *client, union ioctl_arg *arg) @@ -1168,6 +1169,16 @@ static int ioctl_stop_iso(struct client *client, union ioctl_arg *arg) return fw_iso_context_stop(client->iso_context); } +static int ioctl_flush_iso(struct client *client, union ioctl_arg *arg) +{ + struct fw_cdev_flush_iso *a = &arg->flush_iso; + + if (client->iso_context == NULL || a->handle != 0) + return -EINVAL; + + return fw_iso_context_flush_completions(client->iso_context); +} + static int ioctl_get_cycle_timer2(struct client *client, union ioctl_arg *arg) { struct fw_cdev_get_cycle_timer2 *a = &arg->get_cycle_timer2; @@ -1589,6 +1600,7 @@ static int (* const ioctl_handlers[])(struct client *, union ioctl_arg *) = { [0x15] = ioctl_send_phy_packet, [0x16] = ioctl_receive_phy_packets, [0x17] = ioctl_set_iso_channels, + [0x18] = ioctl_flush_iso, }; static int dispatch_ioctl(struct client *client, diff --git a/drivers/firewire/core-iso.c b/drivers/firewire/core-iso.c index 57c3973093ad..2f432a20ce7e 100644 --- a/drivers/firewire/core-iso.c +++ b/drivers/firewire/core-iso.c @@ -191,6 +191,12 @@ void fw_iso_context_queue_flush(struct fw_iso_context *ctx) } EXPORT_SYMBOL(fw_iso_context_queue_flush); +int fw_iso_context_flush_completions(struct fw_iso_context *ctx) +{ + return ctx->card->driver->flush_iso_completions(ctx); +} +EXPORT_SYMBOL(fw_iso_context_flush_completions); + int fw_iso_context_stop(struct fw_iso_context *ctx) { return ctx->card->driver->stop_iso(ctx); diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h index 62f57a4331e3..9047f5547d98 100644 --- a/drivers/firewire/core.h +++ b/drivers/firewire/core.h @@ -106,6 +106,8 @@ struct fw_card_driver { void (*flush_queue_iso)(struct fw_iso_context *ctx); + int (*flush_iso_completions)(struct fw_iso_context *ctx); + int (*stop_iso)(struct fw_iso_context *ctx); }; diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index 632562667a01..59e7894ae3b8 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -172,6 +172,9 @@ struct iso_context { struct context context; void *header; size_t header_length; + unsigned long flushing_completions; + u32 mc_buffer_bus; + u16 mc_completed; u16 last_timestamp; u8 sync; u8 tags; @@ -2749,28 +2752,51 @@ static int handle_ir_buffer_fill(struct context *context, { struct iso_context *ctx = container_of(context, struct iso_context, context); + unsigned int req_count, res_count, completed; u32 buffer_dma; - if (last->res_count != 0) + req_count = le16_to_cpu(last->req_count); + res_count = le16_to_cpu(ACCESS_ONCE(last->res_count)); + completed = req_count - res_count; + buffer_dma = le32_to_cpu(last->data_address); + + if (completed > 0) { + ctx->mc_buffer_bus = buffer_dma; + ctx->mc_completed = completed; + } + + if (res_count != 0) /* Descriptor(s) not done yet, stop iteration */ return 0; - buffer_dma = le32_to_cpu(last->data_address); dma_sync_single_range_for_cpu(context->ohci->card.device, buffer_dma & PAGE_MASK, buffer_dma & ~PAGE_MASK, - le16_to_cpu(last->req_count), - DMA_FROM_DEVICE); + completed, DMA_FROM_DEVICE); - if (last->control & cpu_to_le16(DESCRIPTOR_IRQ_ALWAYS)) + if (last->control & cpu_to_le16(DESCRIPTOR_IRQ_ALWAYS)) { ctx->base.callback.mc(&ctx->base, - le32_to_cpu(last->data_address) + - le16_to_cpu(last->req_count), + buffer_dma + completed, ctx->base.callback_data); + ctx->mc_completed = 0; + } return 1; } +static void flush_ir_buffer_fill(struct iso_context *ctx) +{ + dma_sync_single_range_for_cpu(ctx->context.ohci->card.device, + ctx->mc_buffer_bus & PAGE_MASK, + ctx->mc_buffer_bus & ~PAGE_MASK, + ctx->mc_completed, DMA_FROM_DEVICE); + + ctx->base.callback.mc(&ctx->base, + ctx->mc_buffer_bus + ctx->mc_completed, + ctx->base.callback_data); + ctx->mc_completed = 0; +} + static inline void sync_it_packet_for_cpu(struct context *context, struct descriptor *pd) { @@ -2925,8 +2951,10 @@ static struct fw_iso_context *ohci_allocate_iso_context(struct fw_card *card, if (ret < 0) goto out_with_header; - if (type == FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL) + if (type == FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL) { set_multichannel_mask(ohci, 0); + ctx->mc_completed = 0; + } return &ctx->base; @@ -3388,6 +3416,39 @@ static void ohci_flush_queue_iso(struct fw_iso_context *base) reg_write(ctx->ohci, CONTROL_SET(ctx->regs), CONTEXT_WAKE); } +static int ohci_flush_iso_completions(struct fw_iso_context *base) +{ + struct iso_context *ctx = container_of(base, struct iso_context, base); + int ret = 0; + + tasklet_disable(&ctx->context.tasklet); + + if (!test_and_set_bit_lock(0, &ctx->flushing_completions)) { + context_tasklet((unsigned long)&ctx->context); + + switch (base->type) { + case FW_ISO_CONTEXT_TRANSMIT: + case FW_ISO_CONTEXT_RECEIVE: + if (ctx->header_length != 0) + flush_iso_completions(ctx); + break; + case FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL: + if (ctx->mc_completed != 0) + flush_ir_buffer_fill(ctx); + break; + default: + ret = -ENOSYS; + } + + clear_bit_unlock(0, &ctx->flushing_completions); + smp_mb__after_clear_bit(); + } + + tasklet_enable(&ctx->context.tasklet); + + return ret; +} + static const struct fw_card_driver ohci_driver = { .enable = ohci_enable, .read_phy_reg = ohci_read_phy_reg, @@ -3405,6 +3466,7 @@ static const struct fw_card_driver ohci_driver = { .set_iso_channels = ohci_set_iso_channels, .queue_iso = ohci_queue_iso, .flush_queue_iso = ohci_flush_queue_iso, + .flush_iso_completions = ohci_flush_iso_completions, .start_iso = ohci_start_iso, .stop_iso = ohci_stop_iso, }; diff --git a/include/linux/firewire-cdev.h b/include/linux/firewire-cdev.h index b9bd349c6930..d50036953497 100644 --- a/include/linux/firewire-cdev.h +++ b/include/linux/firewire-cdev.h @@ -212,10 +212,11 @@ struct fw_cdev_event_request2 { * @header: Stripped headers, if any * * This event is sent when the controller has completed an &fw_cdev_iso_packet - * with the %FW_CDEV_ISO_INTERRUPT bit set, or when there have been so many - * completed packets without the interrupt bit set that the kernel's internal - * buffer for @header is about to overflow. (In the latter case, kernels with - * ABI version < 5 drop header data up to the next interrupt packet.) + * with the %FW_CDEV_ISO_INTERRUPT bit set, when explicitly requested with + * %FW_CDEV_IOC_FLUSH_ISO, or when there have been so many completed packets + * without the interrupt bit set that the kernel's internal buffer for @header + * is about to overflow. (In the last case, kernels with ABI version < 5 drop + * header data up to the next interrupt packet.) * * Isochronous transmit events (context type %FW_CDEV_ISO_CONTEXT_TRANSMIT): * @@ -271,7 +272,8 @@ struct fw_cdev_event_iso_interrupt { * This event is sent in multichannel contexts (context type * %FW_CDEV_ISO_CONTEXT_RECEIVE_MULTICHANNEL) for &fw_cdev_iso_packet buffer * chunks that have been completely filled and that have the - * %FW_CDEV_ISO_INTERRUPT bit set. + * %FW_CDEV_ISO_INTERRUPT bit set, or when explicitly requested with + * %FW_CDEV_IOC_FLUSH_ISO. * * The buffer is continuously filled with the following data, per packet: * - the 1394 iso packet header as described at &fw_cdev_event_iso_interrupt, @@ -421,6 +423,9 @@ union fw_cdev_event { #define FW_CDEV_IOC_RECEIVE_PHY_PACKETS _IOW('#', 0x16, struct fw_cdev_receive_phy_packets) #define FW_CDEV_IOC_SET_ISO_CHANNELS _IOW('#', 0x17, struct fw_cdev_set_iso_channels) +/* available since kernel version 3.4 */ +#define FW_CDEV_IOC_FLUSH_ISO _IOW('#', 0x18, struct fw_cdev_flush_iso) + /* * ABI version history * 1 (2.6.22) - initial version @@ -445,6 +450,7 @@ union fw_cdev_event { * %FW_CDEV_IOC_SET_ISO_CHANNELS * 5 (3.4) - send %FW_CDEV_EVENT_ISO_INTERRUPT events when needed to * avoid dropping data + * - added %FW_CDEV_IOC_FLUSH_ISO */ /** @@ -854,6 +860,25 @@ struct fw_cdev_stop_iso { __u32 handle; }; +/** + * struct fw_cdev_flush_iso - flush completed iso packets + * @handle: handle of isochronous context to flush + * + * For %FW_CDEV_ISO_CONTEXT_TRANSMIT or %FW_CDEV_ISO_CONTEXT_RECEIVE contexts, + * report any completed packets. + * + * For %FW_CDEV_ISO_CONTEXT_RECEIVE_MULTICHANNEL contexts, report the current + * offset in the receive buffer, if it has changed; this is typically in the + * middle of some buffer chunk. + * + * Any %FW_CDEV_EVENT_ISO_INTERRUPT or %FW_CDEV_EVENT_ISO_INTERRUPT_MULTICHANNEL + * events generated by this ioctl are sent synchronously, i.e., are available + * for reading from the file descriptor when this ioctl returns. + */ +struct fw_cdev_flush_iso { + __u32 handle; +}; + /** * struct fw_cdev_get_cycle_timer - read cycle timer register * @local_time: system time, in microseconds since the Epoch diff --git a/include/linux/firewire.h b/include/linux/firewire.h index ab5b7a18decf..cdc9b719e9c7 100644 --- a/include/linux/firewire.h +++ b/include/linux/firewire.h @@ -426,6 +426,7 @@ int fw_iso_context_queue(struct fw_iso_context *ctx, struct fw_iso_buffer *buffer, unsigned long payload); void fw_iso_context_queue_flush(struct fw_iso_context *ctx); +int fw_iso_context_flush_completions(struct fw_iso_context *ctx); int fw_iso_context_start(struct fw_iso_context *ctx, int cycle, int sync, int tags); int fw_iso_context_stop(struct fw_iso_context *ctx); -- cgit v1.2.3 From 9080d5d3fa9888facd6898c6705b1a6713882955 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Fri, 9 Mar 2012 06:46:28 -0300 Subject: [media] V4L: Improve the selection API documentation Make the VIDIOC_G/S_SELECTION ioctls documentation more consistent with the rest of media Docbook, use capital letters where necessary and correct few minor errors. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/selection-api.xml | 8 +- .../DocBook/media/v4l/vidioc-g-selection.xml | 106 +++++++++++---------- include/linux/videodev2.h | 30 +++--- 3 files changed, 76 insertions(+), 68 deletions(-) (limited to 'include/linux') diff --git a/Documentation/DocBook/media/v4l/selection-api.xml b/Documentation/DocBook/media/v4l/selection-api.xml index 2f0bdb4d5551..b299e4779354 100644 --- a/Documentation/DocBook/media/v4l/selection-api.xml +++ b/Documentation/DocBook/media/v4l/selection-api.xml @@ -52,6 +52,10 @@ cropping and composing rectangles have the same size. + +For complete list of the available selection targets see table +
@@ -186,7 +190,7 @@ V4L2_SEL_TGT_COMPOSE_ACTIVE target.
- Scaling control. + Scaling control An application can detect if scaling is performed by comparing the width and the height of rectangles obtained using V4L2_SEL_TGT_CROP_ACTIVE @@ -200,7 +204,7 @@ the scaling ratios using these values.
- Comparison with old cropping API. + Comparison with old cropping API The selection API was introduced to cope with deficiencies of previous API , that was designed to control simple capture diff --git a/Documentation/DocBook/media/v4l/vidioc-g-selection.xml b/Documentation/DocBook/media/v4l/vidioc-g-selection.xml index a9d36e0c090e..bb04eff75f45 100644 --- a/Documentation/DocBook/media/v4l/vidioc-g-selection.xml +++ b/Documentation/DocBook/media/v4l/vidioc-g-selection.xml @@ -58,43 +58,43 @@ The ioctls are used to query and configure selection rectangles. - To query the cropping (composing) rectangle set -&v4l2-selection;::type to the respective buffer type. Do not -use multiplanar buffers. Use V4L2_BUF_TYPE_VIDEO_CAPTURE + To query the cropping (composing) rectangle set &v4l2-selection; + type field to the respective buffer type. +Do not use multiplanar buffers. Use V4L2_BUF_TYPE_VIDEO_CAPTURE instead of V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE . Use V4L2_BUF_TYPE_VIDEO_OUTPUT instead of V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE . The next step is -setting &v4l2-selection;::target to value - V4L2_SEL_TGT_CROP_ACTIVE ( +setting the value of &v4l2-selection; target field +to V4L2_SEL_TGT_CROP_ACTIVE ( V4L2_SEL_TGT_COMPOSE_ACTIVE ). Please refer to table or for additional -targets. Fields &v4l2-selection;::flags and - &v4l2-selection;::reserved are ignored and they -must be filled with zeros. The driver fills the rest of the structure or +targets. The flags and reserved + fields of &v4l2-selection; are ignored and they must be filled +with zeros. The driver fills the rest of the structure or returns &EINVAL; if incorrect buffer type or target was used. If cropping (composing) is not supported then the active rectangle is not mutable and it is -always equal to the bounds rectangle. Finally, structure -&v4l2-selection;::r is filled with the current cropping +always equal to the bounds rectangle. Finally, the &v4l2-rect; +r rectangle is filled with the current cropping (composing) coordinates. The coordinates are expressed in driver-dependent units. The only exception are rectangles for images in raw formats, whose coordinates are always expressed in pixels. - To change the cropping (composing) rectangle set -&v4l2-selection;::type to the respective buffer type. Do not + To change the cropping (composing) rectangle set the &v4l2-selection; +type field to the respective buffer type. Do not use multiplanar buffers. Use V4L2_BUF_TYPE_VIDEO_CAPTURE instead of V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE . Use V4L2_BUF_TYPE_VIDEO_OUTPUT instead of V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE . The next step is -setting &v4l2-selection;::target to value - V4L2_SEL_TGT_CROP_ACTIVE ( +setting the value of &v4l2-selection; target to +V4L2_SEL_TGT_CROP_ACTIVE ( V4L2_SEL_TGT_COMPOSE_ACTIVE ). Please refer to table or for additional -targets. Set desired active area into the field -&v4l2-selection;::r . Field -&v4l2-selection;::reserved is ignored and must be filled with -zeros. The driver may adjust the rectangle coordinates. An application may -introduce constraints to control rounding behaviour. Set the field - &v4l2-selection;::flags to one of values: +targets. The &v4l2-rect; r rectangle need to be +set to the desired active area. Field &v4l2-selection; reserved + is ignored and must be filled with zeros. The driver may adjust +coordinates of the requested rectangle. An application may +introduce constraints to control rounding behaviour. The &v4l2-selection; +flags field must be set to one of the following: @@ -129,7 +129,7 @@ and vertical offset and sizes are chosen according to following priority: - Satisfy constraints from &v4l2-selection;::flags. + Satisfy constraints from &v4l2-selection; flags. Adjust width, height, left, and top to hardware limits and alignments. @@ -145,7 +145,7 @@ and vertical offset and sizes are chosen according to following priority: -On success the field &v4l2-selection;::r contains +On success the &v4l2-rect; r field contains the adjusted rectangle. When the parameters are unsuitable the application may modify the cropping (composing) or image parameters and repeat the cycle until satisfactory parameters have been negotiated. If constraints flags have to be @@ -162,38 +162,38 @@ exist no rectangle that satisfies the constraints. V4L2_SEL_TGT_CROP_ACTIVE - 0 - area that is currently cropped by hardware + 0x0000 + The area that is currently cropped by hardware. V4L2_SEL_TGT_CROP_DEFAULT - 1 - suggested cropping rectangle that covers the "whole picture" + 0x0001 + Suggested cropping rectangle that covers the "whole picture". V4L2_SEL_TGT_CROP_BOUNDS - 2 - limits for the cropping rectangle + 0x0002 + Limits for the cropping rectangle. V4L2_SEL_TGT_COMPOSE_ACTIVE - 256 - area to which data are composed by hardware + 0x0100 + The area to which data is composed by hardware. V4L2_SEL_TGT_COMPOSE_DEFAULT - 257 - suggested composing rectangle that covers the "whole picture" + 0x0101 + Suggested composing rectangle that covers the "whole picture". V4L2_SEL_TGT_COMPOSE_BOUNDS - 258 - limits for the composing rectangle + 0x0102 + Limits for the composing rectangle. V4L2_SEL_TGT_COMPOSE_PADDED - 259 - the active area and all padding pixels that are inserted or modified by the hardware + 0x0103 + The active area and all padding pixels that are inserted or modified by hardware. @@ -209,12 +209,14 @@ exist no rectangle that satisfies the constraints. V4L2_SEL_FLAG_GE 0x00000001 - indicate that adjusted rectangle must contain a rectangle from &v4l2-selection;::r + Indicates that the adjusted rectangle must contain the original + &v4l2-selection; r rectangle. V4L2_SEL_FLAG_LE 0x00000002 - indicate that adjusted rectangle must be inside a rectangle from &v4l2-selection;::r + Indicates that the adjusted rectangle must be inside the original + &v4l2-rect; r rectangle. @@ -245,27 +247,29 @@ exist no rectangle that satisfies the constraints. __u32 type - Type of the buffer (from &v4l2-buf-type;) + Type of the buffer (from &v4l2-buf-type;). __u32 target - used to select between cropping and composing rectangles + Used to select between cropping + and composing rectangles. __u32 flags - control over coordinates adjustments, refer to selection flags + Flags controlling the selection rectangle adjustments, refer to + selection flags. &v4l2-rect; r - selection rectangle + The selection rectangle. __u32 reserved[9] - Reserved fields for future use + Reserved fields for future use. @@ -278,24 +282,24 @@ exist no rectangle that satisfies the constraints. EINVAL - The buffer &v4l2-selection;::type -or &v4l2-selection;::target is not supported, or -the &v4l2-selection;::flags are invalid. + Given buffer type type or +the selection target target is not supported, +or the flags argument is not valid. ERANGE - it is not possible to adjust a rectangle -&v4l2-selection;::r that satisfies all contraints from - &v4l2-selection;::flags . + It is not possible to adjust &v4l2-rect; +r rectangle to satisfy all contraints given in the +flags argument. EBUSY - it is not possible to apply change of selection rectangle at the moment. -Usually because streaming is in progress. + It is not possible to apply change of the selection rectangle +at the moment. Usually because streaming is in progress. diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index 3fab6cacccbe..c9c9a4680cc5 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -762,20 +762,20 @@ struct v4l2_crop { /* Selection targets */ -/* current cropping area */ -#define V4L2_SEL_TGT_CROP_ACTIVE 0 -/* default cropping area */ -#define V4L2_SEL_TGT_CROP_DEFAULT 1 -/* cropping bounds */ -#define V4L2_SEL_TGT_CROP_BOUNDS 2 -/* current composing area */ -#define V4L2_SEL_TGT_COMPOSE_ACTIVE 256 -/* default composing area */ -#define V4L2_SEL_TGT_COMPOSE_DEFAULT 257 -/* composing bounds */ -#define V4L2_SEL_TGT_COMPOSE_BOUNDS 258 -/* current composing area plus all padding pixels */ -#define V4L2_SEL_TGT_COMPOSE_PADDED 259 +/* Current cropping area */ +#define V4L2_SEL_TGT_CROP_ACTIVE 0x0000 +/* Default cropping area */ +#define V4L2_SEL_TGT_CROP_DEFAULT 0x0001 +/* Cropping bounds */ +#define V4L2_SEL_TGT_CROP_BOUNDS 0x0002 +/* Current composing area */ +#define V4L2_SEL_TGT_COMPOSE_ACTIVE 0x0100 +/* Default composing area */ +#define V4L2_SEL_TGT_COMPOSE_DEFAULT 0x0101 +/* Composing bounds */ +#define V4L2_SEL_TGT_COMPOSE_BOUNDS 0x0102 +/* Current composing area plus all padding pixels */ +#define V4L2_SEL_TGT_COMPOSE_PADDED 0x0103 /** * struct v4l2_selection - selection info @@ -785,7 +785,7 @@ struct v4l2_crop { * @r: coordinates of selection window * @reserved: for future use, rounds structure size to 64 bytes, set to zero * - * Hardware may use multiple helper window to process a video stream. + * Hardware may use multiple helper windows to process a video stream. * The structure is used to exchange this selection areas between * an application and a driver. */ -- cgit v1.2.3 From e27d359e9b7e446190362cd5c8fe281d02194896 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sun, 18 Mar 2012 14:07:42 -0400 Subject: SUNRPC/NFS: Add Kbuild dependencies for NFS_DEBUG/RPC_DEBUG This allows us to turn on/off the dprintk() debugging interfaces for those distributions that don't ship the 'rpcdebug' utility. It also allows us to add Kbuild dependencies. Specifically, we already know that dprintk() in general relies on CONFIG_SYSCTL. Now it turns out that the NFS dprintks depend on CONFIG_CRC32 after we added support for the filehandle hash. Reported-by: Paul Gortmaker Signed-off-by: Trond Myklebust --- fs/nfs/Kconfig | 6 ++++++ fs/nfs/inode.c | 2 +- fs/nfs/mount_clnt.c | 2 +- fs/nfs/nfsroot.c | 2 +- include/linux/nfs_fs.h | 17 ++++++++--------- include/linux/sunrpc/debug.h | 2 +- net/sunrpc/Kconfig | 13 +++++++++++++ 7 files changed, 31 insertions(+), 13 deletions(-) (limited to 'include/linux') diff --git a/fs/nfs/Kconfig b/fs/nfs/Kconfig index 7bce64c7060e..2a0e6c599147 100644 --- a/fs/nfs/Kconfig +++ b/fs/nfs/Kconfig @@ -144,3 +144,9 @@ config NFS_USE_KERNEL_DNS depends on NFS_V4 && !NFS_USE_LEGACY_DNS select DNS_RESOLVER default y + +config NFS_DEBUG + bool + depends on NFS_FS && SUNRPC_DEBUG + select CRC32 + default y diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 1a19f8d30c14..7bb4d13c1cd5 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -1047,7 +1047,7 @@ struct nfs_fh *nfs_alloc_fhandle(void) return fh; } -#ifdef RPC_DEBUG +#ifdef NFS_DEBUG /* * _nfs_display_fhandle_hash - calculate the crc32 hash for the filehandle * in the same way that wireshark does diff --git a/fs/nfs/mount_clnt.c b/fs/nfs/mount_clnt.c index b37ca34af903..8e65c7f1f87c 100644 --- a/fs/nfs/mount_clnt.c +++ b/fs/nfs/mount_clnt.c @@ -16,7 +16,7 @@ #include #include "internal.h" -#ifdef RPC_DEBUG +#ifdef NFS_DEBUG # define NFSDBG_FACILITY NFSDBG_MOUNT #endif diff --git a/fs/nfs/nfsroot.c b/fs/nfs/nfsroot.c index c4744e1d513c..cd3c910d2d12 100644 --- a/fs/nfs/nfsroot.c +++ b/fs/nfs/nfsroot.c @@ -104,7 +104,7 @@ static char nfs_export_path[NFS_MAXPATHLEN + 1] __initdata = ""; /* server:export path string passed to super.c */ static char nfs_root_device[NFS_MAXPATHLEN + 1] __initdata = ""; -#ifdef RPC_DEBUG +#ifdef NFS_DEBUG /* * When the "nfsrootdebug" kernel command line option is specified, * enable debugging messages for NFSROOT. diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index 0a63ab2b5a76..8f27c2e36ddf 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -38,6 +38,13 @@ #ifdef __KERNEL__ +/* + * Enable dprintk() debugging support for nfs client. + */ +#ifdef CONFIG_NFS_DEBUG +# define NFS_DEBUG +#endif + #include #include #include @@ -391,7 +398,7 @@ static inline void nfs_free_fhandle(const struct nfs_fh *fh) kfree(fh); } -#ifdef RPC_DEBUG +#ifdef NFS_DEBUG extern u32 _nfs_display_fhandle_hash(const struct nfs_fh *fh); static inline u32 nfs_display_fhandle_hash(const struct nfs_fh *fh) { @@ -650,14 +657,6 @@ nfs_fileid_to_ino_t(u64 fileid) #ifdef __KERNEL__ -/* - * Enable debugging support for nfs client. - * Requires RPC_DEBUG. - */ -#ifdef RPC_DEBUG -# define NFS_DEBUG -#endif - # undef ifdebug # ifdef NFS_DEBUG # define ifdebug(fac) if (unlikely(nfs_debug & NFSDBG_##fac)) diff --git a/include/linux/sunrpc/debug.h b/include/linux/sunrpc/debug.h index 6cb2517bcf75..9448eb5e426c 100644 --- a/include/linux/sunrpc/debug.h +++ b/include/linux/sunrpc/debug.h @@ -31,7 +31,7 @@ /* * Enable RPC debugging/profiling. */ -#ifdef CONFIG_SYSCTL +#ifdef CONFIG_SUNRPC_DEBUG #define RPC_DEBUG #endif #ifdef CONFIG_TRACEPOINTS diff --git a/net/sunrpc/Kconfig b/net/sunrpc/Kconfig index ffd243d09188..9fe8857d8d59 100644 --- a/net/sunrpc/Kconfig +++ b/net/sunrpc/Kconfig @@ -39,3 +39,16 @@ config RPCSEC_GSS_KRB5 Kerberos support should be installed. If unsure, say Y. + +config SUNRPC_DEBUG + bool "RPC: Enable dprintk debugging" + depends on SUNRPC && SYSCTL + help + This option enables a sysctl-based debugging interface + that is be used by the 'rpcdebug' utility to turn on or off + logging of different aspects of the kernel RPC activity. + + Disabling this option will make your kernel slightly smaller, + but makes troubleshooting NFS issues significantly harder. + + If unsure, say Y. -- cgit v1.2.3 From b3b536a1230a14e3ef97e5331ba7ad036c6258a9 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Tue, 20 Mar 2012 19:20:53 -0400 Subject: SUNRPC: Kill compiler warning when RPC_DEBUG is unset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Loads of these: linux/net/sunrpc/rpcb_clnt.c:942:2: warning: suggest braces around empty body in ‘do’ statement [-Wempty-body] show up when I unset CONFIG_PROC_SYSCTL. Seen with gcc (GCC) 4.6.1 20110908 (Red Hat 4.6.1-9) Reported-by: Andrew Morton Signed-off-by: Chuck Lever Signed-off-by: Trond Myklebust --- include/linux/sunrpc/debug.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sunrpc/debug.h b/include/linux/sunrpc/debug.h index 9448eb5e426c..a76cc20d98ce 100644 --- a/include/linux/sunrpc/debug.h +++ b/include/linux/sunrpc/debug.h @@ -74,8 +74,8 @@ extern unsigned int nlm_debug; # define RPC_IFDEBUG(x) x #else # define ifdebug(fac) if (0) -# define dfprintk(fac, args...) do ; while (0) -# define dfprintk_rcu(fac, args...) do ; while (0) +# define dfprintk(fac, args...) do {} while (0) +# define dfprintk_rcu(fac, args...) do {} while (0) # define RPC_IFDEBUG(x) #endif -- cgit v1.2.3 From 9304a8120a6ac06d08874d2aec76f52d3376dfe4 Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Tue, 20 Mar 2012 19:26:42 +1100 Subject: nfs: non void functions must return a value Signed-off-by: Stephen Rothwell Signed-off-by: Trond Myklebust --- include/linux/nfs_fs.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include/linux') diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index 8f27c2e36ddf..69ec9cb3a867 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -413,6 +413,7 @@ extern void _nfs_display_fhandle(const struct nfs_fh *fh, const char *caption); #else static inline u32 nfs_display_fhandle_hash(const struct nfs_fh *fh) { + return 0; } static inline void nfs_display_fhandle(const struct nfs_fh *fh, const char *caption) -- cgit v1.2.3 From 6f00866ddd15724eb20eac4ddf6e2c6c1a6cfcdc Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Tue, 20 Mar 2012 14:12:46 -0400 Subject: NFS: Fix more NFS debug related build warnings Signed-off-by: Trond Myklebust --- fs/nfs/blocklayout/blocklayout.c | 5 ++--- fs/nfs/fscache.c | 2 +- fs/nfs/pnfs.h | 8 +++++++- fs/nfs/pnfs_dev.c | 2 ++ include/linux/nfs_fs.h | 2 ++ 5 files changed, 14 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/fs/nfs/blocklayout/blocklayout.c b/fs/nfs/blocklayout/blocklayout.c index 61501346324e..9c94297bb70e 100644 --- a/fs/nfs/blocklayout/blocklayout.c +++ b/fs/nfs/blocklayout/blocklayout.c @@ -233,12 +233,11 @@ bl_read_pagelist(struct nfs_read_data *rdata) sector_t isect, extent_length = 0; struct parallel_io *par; loff_t f_offset = rdata->args.offset; - size_t count = rdata->args.count; struct page **pages = rdata->args.pages; int pg_index = rdata->args.pgbase >> PAGE_CACHE_SHIFT; - dprintk("%s enter nr_pages %u offset %lld count %Zd\n", __func__, - rdata->npages, f_offset, count); + dprintk("%s enter nr_pages %u offset %lld count %u\n", __func__, + rdata->npages, f_offset, (unsigned int)rdata->args.count); par = alloc_parallel(rdata); if (!par) diff --git a/fs/nfs/fscache.c b/fs/nfs/fscache.c index 419119c371bf..ae65c16b3670 100644 --- a/fs/nfs/fscache.c +++ b/fs/nfs/fscache.c @@ -327,7 +327,7 @@ void nfs_fscache_reset_inode_cookie(struct inode *inode) { struct nfs_inode *nfsi = NFS_I(inode); struct nfs_server *nfss = NFS_SERVER(inode); - struct fscache_cookie *old = nfsi->fscache; + NFS_IFDEBUG(struct fscache_cookie *old = nfsi->fscache); nfs_fscache_inode_lock(inode); if (nfsi->fscache) { diff --git a/fs/nfs/pnfs.h b/fs/nfs/pnfs.h index 07802652f5a3..442ebf68eeec 100644 --- a/fs/nfs/pnfs.h +++ b/fs/nfs/pnfs.h @@ -228,7 +228,6 @@ struct nfs4_deviceid_node { atomic_t ref; }; -void nfs4_print_deviceid(const struct nfs4_deviceid *dev_id); struct nfs4_deviceid_node *nfs4_find_get_deviceid(const struct pnfs_layoutdriver_type *, const struct nfs_client *, const struct nfs4_deviceid *); void nfs4_delete_deviceid(const struct pnfs_layoutdriver_type *, const struct nfs_client *, const struct nfs4_deviceid *); void nfs4_init_deviceid_node(struct nfs4_deviceid_node *, @@ -328,6 +327,13 @@ static inline int pnfs_return_layout(struct inode *ino) return 0; } +#ifdef NFS_DEBUG +void nfs4_print_deviceid(const struct nfs4_deviceid *dev_id); +#else +static inline void nfs4_print_deviceid(const struct nfs4_deviceid *dev_id) +{ +} +#endif /* NFS_DEBUG */ #else /* CONFIG_NFS_V4_1 */ static inline void pnfs_destroy_all_layouts(struct nfs_client *clp) diff --git a/fs/nfs/pnfs_dev.c b/fs/nfs/pnfs_dev.c index 6b4cd3849306..73f701f1f4d3 100644 --- a/fs/nfs/pnfs_dev.c +++ b/fs/nfs/pnfs_dev.c @@ -43,6 +43,7 @@ static struct hlist_head nfs4_deviceid_cache[NFS4_DEVICE_ID_HASH_SIZE]; static DEFINE_SPINLOCK(nfs4_deviceid_lock); +#ifdef NFS_DEBUG void nfs4_print_deviceid(const struct nfs4_deviceid *id) { @@ -52,6 +53,7 @@ nfs4_print_deviceid(const struct nfs4_deviceid *id) p[0], p[1], p[2], p[3]); } EXPORT_SYMBOL_GPL(nfs4_print_deviceid); +#endif static inline u32 nfs4_deviceid_hash(const struct nfs4_deviceid *id) diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index 69ec9cb3a867..52a1bdb4ee2b 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -661,8 +661,10 @@ nfs_fileid_to_ino_t(u64 fileid) # undef ifdebug # ifdef NFS_DEBUG # define ifdebug(fac) if (unlikely(nfs_debug & NFSDBG_##fac)) +# define NFS_IFDEBUG(x) x # else # define ifdebug(fac) if (0) +# define NFS_IFDEBUG(x) # endif #endif /* __KERNEL */ -- cgit v1.2.3 From c6cb80d00be42f30716ec817b963bcec094433b5 Mon Sep 17 00:00:00 2001 From: Bryan Schumaker Date: Mon, 19 Mar 2012 14:54:39 -0400 Subject: NFS: Remove nfs4_setup_sequence from generic write code This is an NFS v4 specific operation, so it belongs in the NFS v4 code and not the generic client. Signed-off-by: Bryan Schumaker Signed-off-by: Trond Myklebust --- fs/nfs/direct.c | 4 ---- fs/nfs/nfs3proc.c | 6 ++++++ fs/nfs/nfs4proc.c | 11 +++++++++++ fs/nfs/proc.c | 6 ++++++ fs/nfs/write.c | 15 +-------------- include/linux/nfs_xdr.h | 1 + 6 files changed, 25 insertions(+), 18 deletions(-) (limited to 'include/linux') diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c index 1940f1a56a5f..c4bdaf15289a 100644 --- a/fs/nfs/direct.c +++ b/fs/nfs/direct.c @@ -554,9 +554,7 @@ static void nfs_direct_commit_release(void *calldata) } static const struct rpc_call_ops nfs_commit_direct_ops = { -#if defined(CONFIG_NFS_V4_1) .rpc_call_prepare = nfs_write_prepare, -#endif /* CONFIG_NFS_V4_1 */ .rpc_call_done = nfs_direct_commit_result, .rpc_release = nfs_direct_commit_release, }; @@ -696,9 +694,7 @@ out_unlock: } static const struct rpc_call_ops nfs_write_direct_ops = { -#if defined(CONFIG_NFS_V4_1) .rpc_call_prepare = nfs_write_prepare, -#endif /* CONFIG_NFS_V4_1 */ .rpc_call_done = nfs_direct_write_result, .rpc_release = nfs_direct_write_release, }; diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c index 91943953a370..3bcf722800f3 100644 --- a/fs/nfs/nfs3proc.c +++ b/fs/nfs/nfs3proc.c @@ -828,6 +828,11 @@ static void nfs3_proc_write_setup(struct nfs_write_data *data, struct rpc_messag msg->rpc_proc = &nfs3_procedures[NFS3PROC_WRITE]; } +static void nfs3_proc_write_rpc_prepare(struct rpc_task *task, struct nfs_write_data *data) +{ + rpc_call_start(task); +} + static int nfs3_commit_done(struct rpc_task *task, struct nfs_write_data *data) { if (nfs3_async_handle_jukebox(task, data->inode)) @@ -881,6 +886,7 @@ const struct nfs_rpc_ops nfs_v3_clientops = { .read_setup = nfs3_proc_read_setup, .read_done = nfs3_read_done, .write_setup = nfs3_proc_write_setup, + .write_rpc_prepare = nfs3_proc_write_rpc_prepare, .write_done = nfs3_write_done, .commit_setup = nfs3_proc_commit_setup, .commit_done = nfs3_commit_done, diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index d41d97fb4cb9..dc910691acc0 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -3373,6 +3373,16 @@ static void nfs4_proc_write_setup(struct nfs_write_data *data, struct rpc_messag nfs41_init_sequence(&data->args.seq_args, &data->res.seq_res, 1); } +static void nfs4_proc_write_rpc_prepare(struct rpc_task *task, struct nfs_write_data *data) +{ + if (nfs4_setup_sequence(NFS_SERVER(data->inode), + &data->args.seq_args, + &data->res.seq_res, + task)) + return; + rpc_call_start(task); +} + static int nfs4_commit_done_cb(struct rpc_task *task, struct nfs_write_data *data) { struct inode *inode = data->inode; @@ -6449,6 +6459,7 @@ const struct nfs_rpc_ops nfs_v4_clientops = { .read_setup = nfs4_proc_read_setup, .read_done = nfs4_read_done, .write_setup = nfs4_proc_write_setup, + .write_rpc_prepare = nfs4_proc_write_rpc_prepare, .write_done = nfs4_write_done, .commit_setup = nfs4_proc_commit_setup, .commit_done = nfs4_commit_done, diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c index 0c672588fe5a..8069b41e7f2d 100644 --- a/fs/nfs/proc.c +++ b/fs/nfs/proc.c @@ -668,6 +668,11 @@ static void nfs_proc_write_setup(struct nfs_write_data *data, struct rpc_message msg->rpc_proc = &nfs_procedures[NFSPROC_WRITE]; } +static void nfs_proc_write_rpc_prepare(struct rpc_task *task, struct nfs_write_data *data) +{ + rpc_call_start(task); +} + static void nfs_proc_commit_setup(struct nfs_write_data *data, struct rpc_message *msg) { @@ -738,6 +743,7 @@ const struct nfs_rpc_ops nfs_v2_clientops = { .read_setup = nfs_proc_read_setup, .read_done = nfs_read_done, .write_setup = nfs_proc_write_setup, + .write_rpc_prepare = nfs_proc_write_rpc_prepare, .write_done = nfs_write_done, .commit_setup = nfs_proc_commit_setup, .lock = nfs_proc_lock, diff --git a/fs/nfs/write.c b/fs/nfs/write.c index bd93d40099f9..2c68818f68ac 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -1172,23 +1172,14 @@ out: nfs_writedata_release(calldata); } -#if defined(CONFIG_NFS_V4_1) void nfs_write_prepare(struct rpc_task *task, void *calldata) { struct nfs_write_data *data = calldata; - - if (nfs4_setup_sequence(NFS_SERVER(data->inode), - &data->args.seq_args, - &data->res.seq_res, task)) - return; - rpc_call_start(task); + NFS_PROTO(data->inode)->write_rpc_prepare(task, data); } -#endif /* CONFIG_NFS_V4_1 */ static const struct rpc_call_ops nfs_write_partial_ops = { -#if defined(CONFIG_NFS_V4_1) .rpc_call_prepare = nfs_write_prepare, -#endif /* CONFIG_NFS_V4_1 */ .rpc_call_done = nfs_writeback_done_partial, .rpc_release = nfs_writeback_release_partial, }; @@ -1250,9 +1241,7 @@ remove_request: } static const struct rpc_call_ops nfs_write_full_ops = { -#if defined(CONFIG_NFS_V4_1) .rpc_call_prepare = nfs_write_prepare, -#endif /* CONFIG_NFS_V4_1 */ .rpc_call_done = nfs_writeback_done_full, .rpc_release = nfs_writeback_release_full, }; @@ -1544,9 +1533,7 @@ static void nfs_commit_release(void *calldata) } static const struct rpc_call_ops nfs_commit_ops = { -#if defined(CONFIG_NFS_V4_1) .rpc_call_prepare = nfs_write_prepare, -#endif /* CONFIG_NFS_V4_1 */ .rpc_call_done = nfs_commit_done, .rpc_release = nfs_commit_release, }; diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index c1cf86cceee4..b02a5f9eb217 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -1246,6 +1246,7 @@ struct nfs_rpc_ops { void (*read_setup) (struct nfs_read_data *, struct rpc_message *); int (*read_done) (struct rpc_task *, struct nfs_read_data *); void (*write_setup) (struct nfs_write_data *, struct rpc_message *); + void (*write_rpc_prepare)(struct rpc_task *, struct nfs_write_data *); int (*write_done) (struct rpc_task *, struct nfs_write_data *); void (*commit_setup) (struct nfs_write_data *, struct rpc_message *); int (*commit_done) (struct rpc_task *, struct nfs_write_data *); -- cgit v1.2.3 From ea7c330362257c072791aeaf03bae2cebf9fb984 Mon Sep 17 00:00:00 2001 From: Bryan Schumaker Date: Mon, 19 Mar 2012 14:54:40 -0400 Subject: NFS: Remove nfs4_setup_sequence from generic read code This is an NFS v4 specific operation, so it belongs in the NFS v4 code and not the generic client. Signed-off-by: Bryan Schumaker Signed-off-by: Trond Myklebust --- fs/nfs/direct.c | 2 -- fs/nfs/nfs3proc.c | 6 ++++++ fs/nfs/nfs4proc.c | 11 +++++++++++ fs/nfs/proc.c | 6 ++++++ fs/nfs/read.c | 13 +------------ include/linux/nfs_xdr.h | 1 + 6 files changed, 25 insertions(+), 14 deletions(-) (limited to 'include/linux') diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c index c4bdaf15289a..9c7f66ac6cc2 100644 --- a/fs/nfs/direct.c +++ b/fs/nfs/direct.c @@ -265,9 +265,7 @@ static void nfs_direct_read_release(void *calldata) } static const struct rpc_call_ops nfs_read_direct_ops = { -#if defined(CONFIG_NFS_V4_1) .rpc_call_prepare = nfs_read_prepare, -#endif /* CONFIG_NFS_V4_1 */ .rpc_call_done = nfs_direct_read_result, .rpc_release = nfs_direct_read_release, }; diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c index 3bcf722800f3..9d9b239329dc 100644 --- a/fs/nfs/nfs3proc.c +++ b/fs/nfs/nfs3proc.c @@ -814,6 +814,11 @@ static void nfs3_proc_read_setup(struct nfs_read_data *data, struct rpc_message msg->rpc_proc = &nfs3_procedures[NFS3PROC_READ]; } +static void nfs3_proc_read_rpc_prepare(struct rpc_task *task, struct nfs_read_data *data) +{ + rpc_call_start(task); +} + static int nfs3_write_done(struct rpc_task *task, struct nfs_write_data *data) { if (nfs3_async_handle_jukebox(task, data->inode)) @@ -884,6 +889,7 @@ const struct nfs_rpc_ops nfs_v3_clientops = { .pathconf = nfs3_proc_pathconf, .decode_dirent = nfs3_decode_dirent, .read_setup = nfs3_proc_read_setup, + .read_rpc_prepare = nfs3_proc_read_rpc_prepare, .read_done = nfs3_read_done, .write_setup = nfs3_proc_write_setup, .write_rpc_prepare = nfs3_proc_write_rpc_prepare, diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index dc910691acc0..915385fcf532 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -3299,6 +3299,16 @@ static void nfs4_proc_read_setup(struct nfs_read_data *data, struct rpc_message nfs41_init_sequence(&data->args.seq_args, &data->res.seq_res, 0); } +static void nfs4_proc_read_rpc_prepare(struct rpc_task *task, struct nfs_read_data *data) +{ + if (nfs4_setup_sequence(NFS_SERVER(data->inode), + &data->args.seq_args, + &data->res.seq_res, + task)) + return; + rpc_call_start(task); +} + /* Reset the the nfs_read_data to send the read to the MDS. */ void nfs4_reset_read(struct rpc_task *task, struct nfs_read_data *data) { @@ -6457,6 +6467,7 @@ const struct nfs_rpc_ops nfs_v4_clientops = { .set_capabilities = nfs4_server_capabilities, .decode_dirent = nfs4_decode_dirent, .read_setup = nfs4_proc_read_setup, + .read_rpc_prepare = nfs4_proc_read_rpc_prepare, .read_done = nfs4_read_done, .write_setup = nfs4_proc_write_setup, .write_rpc_prepare = nfs4_proc_write_rpc_prepare, diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c index 8069b41e7f2d..a8df70742d00 100644 --- a/fs/nfs/proc.c +++ b/fs/nfs/proc.c @@ -651,6 +651,11 @@ static void nfs_proc_read_setup(struct nfs_read_data *data, struct rpc_message * msg->rpc_proc = &nfs_procedures[NFSPROC_READ]; } +static void nfs_proc_read_rpc_prepare(struct rpc_task *task, struct nfs_read_data *data) +{ + rpc_call_start(task); +} + static int nfs_write_done(struct rpc_task *task, struct nfs_write_data *data) { if (nfs_async_handle_expired_key(task)) @@ -741,6 +746,7 @@ const struct nfs_rpc_ops nfs_v2_clientops = { .pathconf = nfs_proc_pathconf, .decode_dirent = nfs2_decode_dirent, .read_setup = nfs_proc_read_setup, + .read_rpc_prepare = nfs_proc_read_rpc_prepare, .read_done = nfs_read_done, .write_setup = nfs_proc_write_setup, .write_rpc_prepare = nfs_proc_write_rpc_prepare, diff --git a/fs/nfs/read.c b/fs/nfs/read.c index 2662c0298dd0..cc1f758a7ee1 100644 --- a/fs/nfs/read.c +++ b/fs/nfs/read.c @@ -464,23 +464,14 @@ static void nfs_readpage_release_partial(void *calldata) nfs_readdata_release(calldata); } -#if defined(CONFIG_NFS_V4_1) void nfs_read_prepare(struct rpc_task *task, void *calldata) { struct nfs_read_data *data = calldata; - - if (nfs4_setup_sequence(NFS_SERVER(data->inode), - &data->args.seq_args, &data->res.seq_res, - task)) - return; - rpc_call_start(task); + NFS_PROTO(data->inode)->read_rpc_prepare(task, data); } -#endif /* CONFIG_NFS_V4_1 */ static const struct rpc_call_ops nfs_read_partial_ops = { -#if defined(CONFIG_NFS_V4_1) .rpc_call_prepare = nfs_read_prepare, -#endif /* CONFIG_NFS_V4_1 */ .rpc_call_done = nfs_readpage_result_partial, .rpc_release = nfs_readpage_release_partial, }; @@ -544,9 +535,7 @@ static void nfs_readpage_release_full(void *calldata) } static const struct rpc_call_ops nfs_read_full_ops = { -#if defined(CONFIG_NFS_V4_1) .rpc_call_prepare = nfs_read_prepare, -#endif /* CONFIG_NFS_V4_1 */ .rpc_call_done = nfs_readpage_result_full, .rpc_release = nfs_readpage_release_full, }; diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index b02a5f9eb217..286d74dde053 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -1244,6 +1244,7 @@ struct nfs_rpc_ops { int (*set_capabilities)(struct nfs_server *, struct nfs_fh *); int (*decode_dirent)(struct xdr_stream *, struct nfs_entry *, int); void (*read_setup) (struct nfs_read_data *, struct rpc_message *); + void (*read_rpc_prepare)(struct rpc_task *, struct nfs_read_data *); int (*read_done) (struct rpc_task *, struct nfs_read_data *); void (*write_setup) (struct nfs_write_data *, struct rpc_message *); void (*write_rpc_prepare)(struct rpc_task *, struct nfs_write_data *); -- cgit v1.2.3 From 34e137cc7e3b63c254875e59cd48dcbe6757fe6c Mon Sep 17 00:00:00 2001 From: Bryan Schumaker Date: Mon, 19 Mar 2012 14:54:41 -0400 Subject: NFS: Remove nfs4_setup_sequence from generic unlink code This is an NFS v4 specific operation, so it belongs in the NFS v4 code and not the generic client. Signed-off-by: Bryan Schumaker Signed-off-by: Trond Myklebust --- fs/nfs/nfs3proc.c | 6 ++++++ fs/nfs/nfs4proc.c | 11 +++++++++++ fs/nfs/proc.c | 6 ++++++ fs/nfs/unlink.c | 20 +------------------- include/linux/nfs_xdr.h | 10 ++++++++++ 5 files changed, 34 insertions(+), 19 deletions(-) (limited to 'include/linux') diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c index 9d9b239329dc..7f3f957f677c 100644 --- a/fs/nfs/nfs3proc.c +++ b/fs/nfs/nfs3proc.c @@ -428,6 +428,11 @@ nfs3_proc_unlink_setup(struct rpc_message *msg, struct inode *dir) msg->rpc_proc = &nfs3_procedures[NFS3PROC_REMOVE]; } +static void nfs3_proc_unlink_rpc_prepare(struct rpc_task *task, struct nfs_unlinkdata *data) +{ + rpc_call_start(task); +} + static int nfs3_proc_unlink_done(struct rpc_task *task, struct inode *dir) { @@ -874,6 +879,7 @@ const struct nfs_rpc_ops nfs_v3_clientops = { .create = nfs3_proc_create, .remove = nfs3_proc_remove, .unlink_setup = nfs3_proc_unlink_setup, + .unlink_rpc_prepare = nfs3_proc_unlink_rpc_prepare, .unlink_done = nfs3_proc_unlink_done, .rename = nfs3_proc_rename, .rename_setup = nfs3_proc_rename_setup, diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 915385fcf532..9c247fa7915a 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -2779,6 +2779,16 @@ static void nfs4_proc_unlink_setup(struct rpc_message *msg, struct inode *dir) nfs41_init_sequence(&args->seq_args, &res->seq_res, 1); } +static void nfs4_proc_unlink_rpc_prepare(struct rpc_task *task, struct nfs_unlinkdata *data) +{ + if (nfs4_setup_sequence(NFS_SERVER(data->dir), + &data->args.seq_args, + &data->res.seq_res, + task)) + return; + rpc_call_start(task); +} + static int nfs4_proc_unlink_done(struct rpc_task *task, struct inode *dir) { struct nfs_removeres *res = task->tk_msg.rpc_resp; @@ -6451,6 +6461,7 @@ const struct nfs_rpc_ops nfs_v4_clientops = { .create = nfs4_proc_create, .remove = nfs4_proc_remove, .unlink_setup = nfs4_proc_unlink_setup, + .unlink_rpc_prepare = nfs4_proc_unlink_rpc_prepare, .unlink_done = nfs4_proc_unlink_done, .rename = nfs4_proc_rename, .rename_setup = nfs4_proc_rename_setup, diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c index a8df70742d00..528b9a2fae05 100644 --- a/fs/nfs/proc.c +++ b/fs/nfs/proc.c @@ -358,6 +358,11 @@ nfs_proc_unlink_setup(struct rpc_message *msg, struct inode *dir) msg->rpc_proc = &nfs_procedures[NFSPROC_REMOVE]; } +static void nfs_proc_unlink_rpc_prepare(struct rpc_task *task, struct nfs_unlinkdata *data) +{ + rpc_call_start(task); +} + static int nfs_proc_unlink_done(struct rpc_task *task, struct inode *dir) { if (nfs_async_handle_expired_key(task)) @@ -731,6 +736,7 @@ const struct nfs_rpc_ops nfs_v2_clientops = { .create = nfs_proc_create, .remove = nfs_proc_remove, .unlink_setup = nfs_proc_unlink_setup, + .unlink_rpc_prepare = nfs_proc_unlink_rpc_prepare, .unlink_done = nfs_proc_unlink_done, .rename = nfs_proc_rename, .rename_setup = nfs_proc_rename_setup, diff --git a/fs/nfs/unlink.c b/fs/nfs/unlink.c index fae71c9f5050..9c5a7980e244 100644 --- a/fs/nfs/unlink.c +++ b/fs/nfs/unlink.c @@ -20,15 +20,6 @@ #include "iostat.h" #include "delegation.h" -struct nfs_unlinkdata { - struct hlist_node list; - struct nfs_removeargs args; - struct nfs_removeres res; - struct inode *dir; - struct rpc_cred *cred; - struct nfs_fattr dir_attr; -}; - /** * nfs_free_unlinkdata - release data from a sillydelete operation. * @data: pointer to unlink structure. @@ -107,25 +98,16 @@ static void nfs_async_unlink_release(void *calldata) nfs_sb_deactive(sb); } -#if defined(CONFIG_NFS_V4_1) static void nfs_unlink_prepare(struct rpc_task *task, void *calldata) { struct nfs_unlinkdata *data = calldata; - struct nfs_server *server = NFS_SERVER(data->dir); - - if (nfs4_setup_sequence(server, &data->args.seq_args, - &data->res.seq_res, task)) - return; - rpc_call_start(task); + NFS_PROTO(data->dir)->unlink_rpc_prepare(task, data); } -#endif /* CONFIG_NFS_V4_1 */ static const struct rpc_call_ops nfs_unlink_ops = { .rpc_call_done = nfs_async_unlink_done, .rpc_release = nfs_async_unlink_release, -#if defined(CONFIG_NFS_V4_1) .rpc_call_prepare = nfs_unlink_prepare, -#endif /* CONFIG_NFS_V4_1 */ }; static int nfs_do_call_unlink(struct dentry *parent, struct inode *dir, struct nfs_unlinkdata *data) diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 286d74dde053..8d93e688188d 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -1192,6 +1192,15 @@ struct nfs_write_data { struct page *page_array[NFS_PAGEVEC_SIZE]; }; +struct nfs_unlinkdata { + struct hlist_node list; + struct nfs_removeargs args; + struct nfs_removeres res; + struct inode *dir; + struct rpc_cred *cred; + struct nfs_fattr dir_attr; +}; + struct nfs_access_entry; struct nfs_client; struct rpc_timeout; @@ -1221,6 +1230,7 @@ struct nfs_rpc_ops { struct iattr *, int, struct nfs_open_context *); int (*remove) (struct inode *, struct qstr *); void (*unlink_setup) (struct rpc_message *, struct inode *dir); + void (*unlink_rpc_prepare) (struct rpc_task *, struct nfs_unlinkdata *); int (*unlink_done) (struct rpc_task *, struct inode *); int (*rename) (struct inode *, struct qstr *, struct inode *, struct qstr *); -- cgit v1.2.3 From c6bfa1a16377b42496ecc0490a33516c0e414e7b Mon Sep 17 00:00:00 2001 From: Bryan Schumaker Date: Mon, 19 Mar 2012 14:54:42 -0400 Subject: NFS: Remove nfs4_setup_sequence from generic rename code This is an NFS v4 specific operation, so it belongs in the NFS v4 code and not the generic client. Signed-off-by: Bryan Schumaker Signed-off-by: Trond Myklebust --- fs/nfs/nfs3proc.c | 6 ++++++ fs/nfs/nfs4proc.c | 11 +++++++++++ fs/nfs/proc.c | 6 ++++++ fs/nfs/unlink.c | 23 +---------------------- include/linux/nfs_xdr.h | 13 +++++++++++++ 5 files changed, 37 insertions(+), 22 deletions(-) (limited to 'include/linux') diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c index 7f3f957f677c..5242eae6711a 100644 --- a/fs/nfs/nfs3proc.c +++ b/fs/nfs/nfs3proc.c @@ -450,6 +450,11 @@ nfs3_proc_rename_setup(struct rpc_message *msg, struct inode *dir) msg->rpc_proc = &nfs3_procedures[NFS3PROC_RENAME]; } +static void nfs3_proc_rename_rpc_prepare(struct rpc_task *task, struct nfs_renamedata *data) +{ + rpc_call_start(task); +} + static int nfs3_proc_rename_done(struct rpc_task *task, struct inode *old_dir, struct inode *new_dir) @@ -883,6 +888,7 @@ const struct nfs_rpc_ops nfs_v3_clientops = { .unlink_done = nfs3_proc_unlink_done, .rename = nfs3_proc_rename, .rename_setup = nfs3_proc_rename_setup, + .rename_rpc_prepare = nfs3_proc_rename_rpc_prepare, .rename_done = nfs3_proc_rename_done, .link = nfs3_proc_link, .symlink = nfs3_proc_symlink, diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 9c247fa7915a..b76dd0efae75 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -2814,6 +2814,16 @@ static void nfs4_proc_rename_setup(struct rpc_message *msg, struct inode *dir) nfs41_init_sequence(&arg->seq_args, &res->seq_res, 1); } +static void nfs4_proc_rename_rpc_prepare(struct rpc_task *task, struct nfs_renamedata *data) +{ + if (nfs4_setup_sequence(NFS_SERVER(data->old_dir), + &data->args.seq_args, + &data->res.seq_res, + task)) + return; + rpc_call_start(task); +} + static int nfs4_proc_rename_done(struct rpc_task *task, struct inode *old_dir, struct inode *new_dir) { @@ -6465,6 +6475,7 @@ const struct nfs_rpc_ops nfs_v4_clientops = { .unlink_done = nfs4_proc_unlink_done, .rename = nfs4_proc_rename, .rename_setup = nfs4_proc_rename_setup, + .rename_rpc_prepare = nfs4_proc_rename_rpc_prepare, .rename_done = nfs4_proc_rename_done, .link = nfs4_proc_link, .symlink = nfs4_proc_symlink, diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c index 528b9a2fae05..b63b6f4d14fb 100644 --- a/fs/nfs/proc.c +++ b/fs/nfs/proc.c @@ -377,6 +377,11 @@ nfs_proc_rename_setup(struct rpc_message *msg, struct inode *dir) msg->rpc_proc = &nfs_procedures[NFSPROC_RENAME]; } +static void nfs_proc_rename_rpc_prepare(struct rpc_task *task, struct nfs_renamedata *data) +{ + rpc_call_start(task); +} + static int nfs_proc_rename_done(struct rpc_task *task, struct inode *old_dir, struct inode *new_dir) @@ -740,6 +745,7 @@ const struct nfs_rpc_ops nfs_v2_clientops = { .unlink_done = nfs_proc_unlink_done, .rename = nfs_proc_rename, .rename_setup = nfs_proc_rename_setup, + .rename_rpc_prepare = nfs_proc_rename_rpc_prepare, .rename_done = nfs_proc_rename_done, .link = nfs_proc_link, .symlink = nfs_proc_symlink, diff --git a/fs/nfs/unlink.c b/fs/nfs/unlink.c index 9c5a7980e244..3210a03342f9 100644 --- a/fs/nfs/unlink.c +++ b/fs/nfs/unlink.c @@ -323,18 +323,6 @@ nfs_cancel_async_unlink(struct dentry *dentry) spin_unlock(&dentry->d_lock); } -struct nfs_renamedata { - struct nfs_renameargs args; - struct nfs_renameres res; - struct rpc_cred *cred; - struct inode *old_dir; - struct dentry *old_dentry; - struct nfs_fattr old_fattr; - struct inode *new_dir; - struct dentry *new_dentry; - struct nfs_fattr new_fattr; -}; - /** * nfs_async_rename_done - Sillyrename post-processing * @task: rpc_task of the sillyrename @@ -385,25 +373,16 @@ static void nfs_async_rename_release(void *calldata) kfree(data); } -#if defined(CONFIG_NFS_V4_1) static void nfs_rename_prepare(struct rpc_task *task, void *calldata) { struct nfs_renamedata *data = calldata; - struct nfs_server *server = NFS_SERVER(data->old_dir); - - if (nfs4_setup_sequence(server, &data->args.seq_args, - &data->res.seq_res, task)) - return; - rpc_call_start(task); + NFS_PROTO(data->old_dir)->rename_rpc_prepare(task, data); } -#endif /* CONFIG_NFS_V4_1 */ static const struct rpc_call_ops nfs_rename_ops = { .rpc_call_done = nfs_async_rename_done, .rpc_release = nfs_async_rename_release, -#if defined(CONFIG_NFS_V4_1) .rpc_call_prepare = nfs_rename_prepare, -#endif /* CONFIG_NFS_V4_1 */ }; /** diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 8d93e688188d..bfd0d1bf6707 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -1201,6 +1201,18 @@ struct nfs_unlinkdata { struct nfs_fattr dir_attr; }; +struct nfs_renamedata { + struct nfs_renameargs args; + struct nfs_renameres res; + struct rpc_cred *cred; + struct inode *old_dir; + struct dentry *old_dentry; + struct nfs_fattr old_fattr; + struct inode *new_dir; + struct dentry *new_dentry; + struct nfs_fattr new_fattr; +}; + struct nfs_access_entry; struct nfs_client; struct rpc_timeout; @@ -1235,6 +1247,7 @@ struct nfs_rpc_ops { int (*rename) (struct inode *, struct qstr *, struct inode *, struct qstr *); void (*rename_setup) (struct rpc_message *msg, struct inode *dir); + void (*rename_rpc_prepare)(struct rpc_task *task, struct nfs_renamedata *); int (*rename_done) (struct rpc_task *task, struct inode *old_dir, struct inode *new_dir); int (*link) (struct inode *, struct inode *, struct qstr *); int (*symlink) (struct inode *, struct dentry *, struct page *, -- cgit v1.2.3 From 9d547c35799a4ddd235f1565cec2fff6c9263504 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Wed, 21 Mar 2012 22:34:55 -0400 Subject: vfs: remove unused superblock helpers Remove the 'sb_mark_dirty()', 'sb_mark_clean()' and 'sb_is_dirty()' helpers which are not used. I introduced them 2 years and the intention was to make all file-systems use them in order to be able to optimize 'sync_supers()'. However, Al Viro vetoed my patches at the end and asked me to push superblock management down to file-systems and get rid of the 's_dirt' flag completely, as well as kill 'sync_supers()' altogether. Thus, remove the helpers. Signed-off-by: Artem Bityutskiy Cc: Al Viro Signed-off-by: "Theodore Ts'o" --- include/linux/fs.h | 13 ------------- 1 file changed, 13 deletions(-) (limited to 'include/linux') diff --git a/include/linux/fs.h b/include/linux/fs.h index 386da09f229d..526072c073f7 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1870,19 +1870,6 @@ extern struct dentry *mount_pseudo(struct file_system_type *, char *, const struct dentry_operations *dops, unsigned long); -static inline void sb_mark_dirty(struct super_block *sb) -{ - sb->s_dirt = 1; -} -static inline void sb_mark_clean(struct super_block *sb) -{ - sb->s_dirt = 0; -} -static inline int sb_is_dirty(struct super_block *sb) -{ - return sb->s_dirt; -} - /* Alas, no aliases. Too much hassle with bringing module.h everywhere */ #define fops_get(fops) \ (((fops) && try_module_get((fops)->owner) ? (fops) : NULL)) -- cgit v1.2.3 From 5766651971e81298732466c9aa462ff47898ba37 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 23 Jan 2012 15:49:27 -0600 Subject: ceph: use a shared zero page rather than one per messenger Each messenger allocates a page to be used when writing zeroes out in the event of error or other abnormal condition. Instead, use the kernel ZERO_PAGE() for that purpose. Signed-off-by: Alex Elder Signed-off-by: Sage Weil --- include/linux/ceph/messenger.h | 1 - net/ceph/messenger.c | 43 ++++++++++++++++++++++++++++-------------- 2 files changed, 29 insertions(+), 15 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ceph/messenger.h b/include/linux/ceph/messenger.h index ffbeb2c217b4..6b5af5f976d1 100644 --- a/include/linux/ceph/messenger.h +++ b/include/linux/ceph/messenger.h @@ -54,7 +54,6 @@ struct ceph_connection_operations { struct ceph_messenger { struct ceph_entity_inst inst; /* my name+address */ struct ceph_entity_addr my_enc_addr; - struct page *zero_page; /* used in certain error cases */ bool nocrc; diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c index d11f91b05452..738356255e0b 100644 --- a/net/ceph/messenger.c +++ b/net/ceph/messenger.c @@ -52,6 +52,9 @@ static char addr_str[MAX_ADDR_STR][MAX_ADDR_STR_LEN]; static DEFINE_SPINLOCK(addr_str_lock); static int last_addr_str; +static struct page *zero_page; /* used in certain error cases */ +static void *zero_page_address; /* kernel virtual addr of zero_page */ + const char *ceph_pr_addr(const struct sockaddr_storage *ss) { int i; @@ -99,18 +102,41 @@ struct workqueue_struct *ceph_msgr_wq; int ceph_msgr_init(void) { + BUG_ON(zero_page != NULL); + zero_page = ZERO_PAGE(0); + page_cache_get(zero_page); + + BUG_ON(zero_page_address != NULL); + zero_page_address = kmap(zero_page); + ceph_msgr_wq = alloc_workqueue("ceph-msgr", WQ_NON_REENTRANT, 0); if (!ceph_msgr_wq) { pr_err("msgr_init failed to create workqueue\n"); + + zero_page_address = NULL; + kunmap(zero_page); + page_cache_release(zero_page); + zero_page = NULL; + return -ENOMEM; } + return 0; } EXPORT_SYMBOL(ceph_msgr_init); void ceph_msgr_exit(void) { + BUG_ON(ceph_msgr_wq == NULL); destroy_workqueue(ceph_msgr_wq); + + BUG_ON(zero_page_address == NULL); + zero_page_address = NULL; + + BUG_ON(zero_page == NULL); + kunmap(zero_page); + page_cache_release(zero_page); + zero_page = NULL; } EXPORT_SYMBOL(ceph_msgr_exit); @@ -841,9 +867,9 @@ static int write_partial_msg_pages(struct ceph_connection *con) max_write = bv->bv_len; #endif } else { - page = con->msgr->zero_page; + page = zero_page; if (crc) - kaddr = page_address(con->msgr->zero_page); + kaddr = zero_page_address; } len = min_t(int, max_write - con->out_msg_pos.page_pos, total_max_write); @@ -914,7 +940,7 @@ static int write_partial_skip(struct ceph_connection *con) while (con->out_skip > 0) { struct kvec iov = { - .iov_base = page_address(con->msgr->zero_page), + .iov_base = zero_page_address, .iov_len = min(con->out_skip, (int)PAGE_CACHE_SIZE) }; @@ -2222,15 +2248,6 @@ struct ceph_messenger *ceph_messenger_create(struct ceph_entity_addr *myaddr, spin_lock_init(&msgr->global_seq_lock); - /* the zero page is needed if a request is "canceled" while the message - * is being written over the socket */ - msgr->zero_page = __page_cache_alloc(GFP_KERNEL | __GFP_ZERO); - if (!msgr->zero_page) { - kfree(msgr); - return ERR_PTR(-ENOMEM); - } - kmap(msgr->zero_page); - if (myaddr) msgr->inst.addr = *myaddr; @@ -2247,8 +2264,6 @@ EXPORT_SYMBOL(ceph_messenger_create); void ceph_messenger_destroy(struct ceph_messenger *msgr) { dout("destroy %p\n", msgr); - kunmap(msgr->zero_page); - __free_page(msgr->zero_page); kfree(msgr); dout("destroyed messenger %p\n", msgr); } -- cgit v1.2.3 From ee57741c5209154b8ef124bcaa2496da1b69a988 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 24 Jan 2012 10:08:36 -0600 Subject: rbd: make ceph_parse_options() return a pointer ceph_parse_options() takes the address of a pointer as an argument and uses it to return the address of an allocated structure if successful. With this interface is not evident at call sites that the pointer is always initialized. Change the interface to return the address instead (or a pointer-coded error code) to make the validity of the returned pointer obvious. Signed-off-by: Alex Elder Signed-off-by: Sage Weil --- drivers/block/rbd.c | 6 ++++-- fs/ceph/super.c | 6 ++++-- include/linux/ceph/libceph.h | 2 +- net/ceph/ceph_common.c | 16 ++++++++-------- 4 files changed, 17 insertions(+), 13 deletions(-) (limited to 'include/linux') diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index b9371f0b9532..ed6711e35323 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -371,11 +371,13 @@ static int rbd_get_client(struct rbd_device *rbd_dev, const char *mon_addr, rbd_opts->notify_timeout = RBD_NOTIFY_TIMEOUT_DEFAULT; - ret = ceph_parse_options(&opt, options, mon_addr, + opt = ceph_parse_options(options, mon_addr, mon_addr + strlen(mon_addr), parse_rbd_opts_token, rbd_opts); - if (ret < 0) + if (IS_ERR(opt)) { + ret = PTR_ERR(opt); goto done_err; + } spin_lock(&node_lock); rbdc = __rbd_client_find(opt); diff --git a/fs/ceph/super.c b/fs/ceph/super.c index c3da3b32bdde..4fab1fdcfa6a 100644 --- a/fs/ceph/super.c +++ b/fs/ceph/super.c @@ -334,10 +334,12 @@ static int parse_mount_options(struct ceph_mount_options **pfsopt, *path += 2; dout("server path '%s'\n", *path); - err = ceph_parse_options(popt, options, dev_name, dev_name_end, + *popt = ceph_parse_options(options, dev_name, dev_name_end, parse_fsopt_token, (void *)fsopt); - if (err) + if (IS_ERR(*popt)) { + err = PTR_ERR(*popt); goto out; + } /* success */ *pfsopt = fsopt; diff --git a/include/linux/ceph/libceph.h b/include/linux/ceph/libceph.h index 95bd8502e715..92eef7c3d3c5 100644 --- a/include/linux/ceph/libceph.h +++ b/include/linux/ceph/libceph.h @@ -207,7 +207,7 @@ extern struct kmem_cache *ceph_cap_cachep; extern struct kmem_cache *ceph_dentry_cachep; extern struct kmem_cache *ceph_file_cachep; -extern int ceph_parse_options(struct ceph_options **popt, char *options, +extern struct ceph_options *ceph_parse_options(char *options, const char *dev_name, const char *dev_name_end, int (*parse_extra_token)(char *c, void *private), void *private); diff --git a/net/ceph/ceph_common.c b/net/ceph/ceph_common.c index 761ad9d6cc3b..621c3221b393 100644 --- a/net/ceph/ceph_common.c +++ b/net/ceph/ceph_common.c @@ -277,10 +277,11 @@ out: return err; } -int ceph_parse_options(struct ceph_options **popt, char *options, - const char *dev_name, const char *dev_name_end, - int (*parse_extra_token)(char *c, void *private), - void *private) +struct ceph_options * +ceph_parse_options(char *options, const char *dev_name, + const char *dev_name_end, + int (*parse_extra_token)(char *c, void *private), + void *private) { struct ceph_options *opt; const char *c; @@ -289,7 +290,7 @@ int ceph_parse_options(struct ceph_options **popt, char *options, opt = kzalloc(sizeof(*opt), GFP_KERNEL); if (!opt) - return err; + return ERR_PTR(-ENOMEM); opt->mon_addr = kcalloc(CEPH_MAX_MON, sizeof(*opt->mon_addr), GFP_KERNEL); if (!opt->mon_addr) @@ -412,12 +413,11 @@ int ceph_parse_options(struct ceph_options **popt, char *options, } /* success */ - *popt = opt; - return 0; + return opt; out: ceph_destroy_options(opt); - return err; + return ERR_PTR(err); } EXPORT_SYMBOL(ceph_parse_options); -- cgit v1.2.3 From e0f43c9419c1900e5b50de4261e9686a45a0a2b8 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 14 Feb 2012 14:05:33 -0600 Subject: libceph: make ceph_msgr_wq private The messenger workqueue has no need to be public. So give it static scope. Signed-off-by: Alex Elder Signed-off-by: Sage Weil --- include/linux/ceph/messenger.h | 2 -- net/ceph/messenger.c | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ceph/messenger.h b/include/linux/ceph/messenger.h index 6b5af5f976d1..5ca0f8244203 100644 --- a/include/linux/ceph/messenger.h +++ b/include/linux/ceph/messenger.h @@ -14,8 +14,6 @@ struct ceph_msg; struct ceph_connection; -extern struct workqueue_struct *ceph_msgr_wq; /* receive work queue */ - /* * Ceph defines these callbacks for handling connection events. */ diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c index 04d2b975ab0c..31f59ac03d8a 100644 --- a/net/ceph/messenger.c +++ b/net/ceph/messenger.c @@ -97,7 +97,7 @@ static void encode_my_addr(struct ceph_messenger *msgr) /* * work queue for all reading and writing to/from the socket. */ -struct workqueue_struct *ceph_msgr_wq; +static struct workqueue_struct *ceph_msgr_wq; int ceph_msgr_init(void) { -- cgit v1.2.3 From bca064d236a2e3162a07c758855221bcbe3c475b Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 15 Feb 2012 07:43:54 -0600 Subject: libceph: use "do" in CRC-related Boolean variables Change the name (and type) of a few CRC-related Boolean local variables so they contain the word "do", to distingish their purpose from variables used for holding an actual CRC value. Note that in the process of doing this I identified a fairly serious logic error in write_partial_msg_pages(): the value of "do_crc" assigned appears to be the opposite of what it should be. No attempt to fix this is made here; this change preserves the erroneous behavior. The problem I found is documented here: http://tracker.newdream.net/issues/2064 Signed-off-by: Alex Elder Signed-off-by: Sage Weil --- include/linux/ceph/messenger.h | 2 +- net/ceph/messenger.c | 40 ++++++++++++++++++++-------------------- 2 files changed, 21 insertions(+), 21 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ceph/messenger.h b/include/linux/ceph/messenger.h index 5ca0f8244203..3bff047f6b0f 100644 --- a/include/linux/ceph/messenger.h +++ b/include/linux/ceph/messenger.h @@ -98,7 +98,7 @@ struct ceph_msg { struct ceph_msg_pos { int page, page_pos; /* which page; offset in page */ int data_pos; /* offset in data payload */ - int did_page_crc; /* true if we've calculated crc for current page */ + bool did_page_crc; /* true if we've calculated crc for current page */ }; /* ceph connection fault delay defaults, for exponential backoff */ diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c index 44d8c77cabdd..204e229e6628 100644 --- a/net/ceph/messenger.c +++ b/net/ceph/messenger.c @@ -595,7 +595,7 @@ static void prepare_write_message(struct ceph_connection *con) else con->out_msg_pos.page_pos = 0; con->out_msg_pos.data_pos = 0; - con->out_msg_pos.did_page_crc = 0; + con->out_msg_pos.did_page_crc = false; con->out_more = 1; /* data + footer will follow */ } else { /* no, queue up footer too and be done */ @@ -805,7 +805,7 @@ static int write_partial_msg_pages(struct ceph_connection *con) struct ceph_msg *msg = con->out_msg; unsigned data_len = le32_to_cpu(msg->hdr.data_len); size_t len; - int crc = con->msgr->nocrc; + bool do_crc = con->msgr->nocrc; int ret; int total_max_write; int in_trail = 0; @@ -843,17 +843,17 @@ static int write_partial_msg_pages(struct ceph_connection *con) page = list_first_entry(&msg->trail->head, struct page, lru); - if (crc) + if (do_crc) kaddr = kmap(page); max_write = PAGE_SIZE; } else if (msg->pages) { page = msg->pages[con->out_msg_pos.page]; - if (crc) + if (do_crc) kaddr = kmap(page); } else if (msg->pagelist) { page = list_first_entry(&msg->pagelist->head, struct page, lru); - if (crc) + if (do_crc) kaddr = kmap(page); #ifdef CONFIG_BLOCK } else if (msg->bio) { @@ -862,26 +862,26 @@ static int write_partial_msg_pages(struct ceph_connection *con) bv = bio_iovec_idx(msg->bio_iter, msg->bio_seg); page = bv->bv_page; page_shift = bv->bv_offset; - if (crc) + if (do_crc) kaddr = kmap(page) + page_shift; max_write = bv->bv_len; #endif } else { page = zero_page; - if (crc) + if (do_crc) kaddr = zero_page_address; } len = min_t(int, max_write - con->out_msg_pos.page_pos, total_max_write); - if (crc && !con->out_msg_pos.did_page_crc) { + if (do_crc && !con->out_msg_pos.did_page_crc) { void *base = kaddr + con->out_msg_pos.page_pos; u32 tmpcrc = le32_to_cpu(con->out_msg->footer.data_crc); BUG_ON(kaddr == NULL); con->out_msg->footer.data_crc = cpu_to_le32(crc32c(tmpcrc, base, len)); - con->out_msg_pos.did_page_crc = 1; + con->out_msg_pos.did_page_crc = true; } ret = kernel_sendpage(con->sock, page, con->out_msg_pos.page_pos + page_shift, @@ -889,7 +889,7 @@ static int write_partial_msg_pages(struct ceph_connection *con) MSG_DONTWAIT | MSG_NOSIGNAL | MSG_MORE); - if (crc && + if (do_crc && (msg->pages || msg->pagelist || msg->bio || in_trail)) kunmap(page); @@ -903,7 +903,7 @@ static int write_partial_msg_pages(struct ceph_connection *con) if (ret == len) { con->out_msg_pos.page_pos = 0; con->out_msg_pos.page++; - con->out_msg_pos.did_page_crc = 0; + con->out_msg_pos.did_page_crc = false; if (in_trail) list_move_tail(&page->lru, &msg->trail->head); @@ -920,7 +920,7 @@ static int write_partial_msg_pages(struct ceph_connection *con) dout("write_partial_msg_pages %p msg %p done\n", con, msg); /* prepare and queue up footer, too */ - if (!crc) + if (!do_crc) con->out_msg->footer.flags |= CEPH_MSG_FOOTER_NOCRC; ceph_con_out_kvec_reset(con); prepare_write_message_footer(con); @@ -1557,7 +1557,7 @@ static struct ceph_msg *ceph_alloc_msg(struct ceph_connection *con, static int read_partial_message_pages(struct ceph_connection *con, struct page **pages, - unsigned data_len, int datacrc) + unsigned data_len, bool do_datacrc) { void *p; int ret; @@ -1570,7 +1570,7 @@ static int read_partial_message_pages(struct ceph_connection *con, p = kmap(pages[con->in_msg_pos.page]); ret = ceph_tcp_recvmsg(con->sock, p + con->in_msg_pos.page_pos, left); - if (ret > 0 && datacrc) + if (ret > 0 && do_datacrc) con->in_data_crc = crc32c(con->in_data_crc, p + con->in_msg_pos.page_pos, ret); @@ -1590,7 +1590,7 @@ static int read_partial_message_pages(struct ceph_connection *con, #ifdef CONFIG_BLOCK static int read_partial_message_bio(struct ceph_connection *con, struct bio **bio_iter, int *bio_seg, - unsigned data_len, int datacrc) + unsigned data_len, bool do_datacrc) { struct bio_vec *bv = bio_iovec_idx(*bio_iter, *bio_seg); void *p; @@ -1606,7 +1606,7 @@ static int read_partial_message_bio(struct ceph_connection *con, ret = ceph_tcp_recvmsg(con->sock, p + con->in_msg_pos.page_pos, left); - if (ret > 0 && datacrc) + if (ret > 0 && do_datacrc) con->in_data_crc = crc32c(con->in_data_crc, p + con->in_msg_pos.page_pos, ret); @@ -1633,7 +1633,7 @@ static int read_partial_message(struct ceph_connection *con) int ret; int to, left; unsigned front_len, middle_len, data_len; - int datacrc = con->msgr->nocrc; + bool do_datacrc = con->msgr->nocrc; int skip; u64 seq; @@ -1744,7 +1744,7 @@ static int read_partial_message(struct ceph_connection *con) while (con->in_msg_pos.data_pos < data_len) { if (m->pages) { ret = read_partial_message_pages(con, m->pages, - data_len, datacrc); + data_len, do_datacrc); if (ret <= 0) return ret; #ifdef CONFIG_BLOCK @@ -1752,7 +1752,7 @@ static int read_partial_message(struct ceph_connection *con) ret = read_partial_message_bio(con, &m->bio_iter, &m->bio_seg, - data_len, datacrc); + data_len, do_datacrc); if (ret <= 0) return ret; #endif @@ -1787,7 +1787,7 @@ static int read_partial_message(struct ceph_connection *con) m, con->in_middle_crc, m->footer.middle_crc); return -EBADMSG; } - if (datacrc && + if (do_datacrc && (m->footer.flags & CEPH_MSG_FOOTER_NOCRC) == 0 && con->in_data_crc != le32_to_cpu(m->footer.data_crc)) { pr_err("read_partial_message %p data crc %u != exp. %u\n", m, -- cgit v1.2.3 From b502bd1152472dc1b98c60434f23c23b280c7b94 Mon Sep 17 00:00:00 2001 From: Muthu Kumar Date: Fri, 23 Mar 2012 15:01:50 -0700 Subject: magic.h: move some FS magic numbers into magic.h - Move open-coded filesystem magic numbers into magic.h - Rearrange magic.h so that the filesystem-related constants are grouped together. Signed-off-by: Muthukumar R Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/mtd/mtdchar.c | 2 +- fs/binfmt_misc.c | 3 ++- fs/block_dev.c | 3 ++- fs/pipe.c | 1 + include/linux/magic.h | 18 ++++++++++++------ include/linux/pipe_fs_i.h | 2 -- 6 files changed, 18 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index 50c6a1e7f675..c57ae92ebda4 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -31,13 +31,13 @@ #include #include #include +#include #include #include #include #include -#define MTD_INODE_FS_MAGIC 0x11307854 static DEFINE_MUTEX(mtd_mutex); static struct vfsmount *mtd_inode_mnt __read_mostly; diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c index 1ffb60355cae..613aa0618235 100644 --- a/fs/binfmt_misc.c +++ b/fs/binfmt_misc.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -699,7 +700,7 @@ static int bm_fill_super(struct super_block * sb, void * data, int silent) [3] = {"register", &bm_register_operations, S_IWUSR}, /* last one */ {""} }; - int err = simple_fill_super(sb, 0x42494e4d, bm_files); + int err = simple_fill_super(sb, BINFMTFS_MAGIC, bm_files); if (!err) sb->s_op = &s_ops; return err; diff --git a/fs/block_dev.c b/fs/block_dev.c index a9ff3000b83d..e08f6a20a5bb 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -506,7 +507,7 @@ static const struct super_operations bdev_sops = { static struct dentry *bd_mount(struct file_system_type *fs_type, int flags, const char *dev_name, void *data) { - return mount_pseudo(fs_type, "bdev:", &bdev_sops, NULL, 0x62646576); + return mount_pseudo(fs_type, "bdev:", &bdev_sops, NULL, BDEVFS_MAGIC); } static struct file_system_type bd_type = { diff --git a/fs/pipe.c b/fs/pipe.c index fe0502f9beb2..25feaa3faac0 100644 --- a/fs/pipe.c +++ b/fs/pipe.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include diff --git a/include/linux/magic.h b/include/linux/magic.h index b7ed4759dbb2..e15192cb9cf4 100644 --- a/include/linux/magic.h +++ b/include/linux/magic.h @@ -9,7 +9,6 @@ #define CRAMFS_MAGIC 0x28cd3d45 /* some random number */ #define CRAMFS_MAGIC_WEND 0x453dcd28 /* magic number with the wrong endianess */ #define DEBUGFS_MAGIC 0x64626720 -#define SYSFS_MAGIC 0x62656572 #define SECURITYFS_MAGIC 0x73636673 #define SELINUX_MAGIC 0xf97cff8c #define RAMFS_MAGIC 0x858458f6 /* some random number */ @@ -27,7 +26,6 @@ #define HPFS_SUPER_MAGIC 0xf995e849 #define ISOFS_SUPER_MAGIC 0x9660 #define JFFS2_SUPER_MAGIC 0x72b6 -#define ANON_INODE_FS_MAGIC 0x09041934 #define PSTOREFS_MAGIC 0x6165676C #define MINIX_SUPER_MAGIC 0x137F /* minix v1 fs, 14 char names */ @@ -40,7 +38,6 @@ #define NCP_SUPER_MAGIC 0x564c /* Guess, what 0x564c is :-) */ #define NFS_SUPER_MAGIC 0x6969 #define OPENPROM_SUPER_MAGIC 0x9fa1 -#define PROC_SUPER_MAGIC 0x9fa0 #define QNX4_SUPER_MAGIC 0x002f /* qnx4 fs detection */ #define QNX6_SUPER_MAGIC 0x68191122 /* qnx6 fs detection */ @@ -52,15 +49,24 @@ #define REISER2FS_JR_SUPER_MAGIC_STRING "ReIsEr3Fs" #define SMB_SUPER_MAGIC 0x517B -#define USBDEVICE_SUPER_MAGIC 0x9fa2 #define CGROUP_SUPER_MAGIC 0x27e0eb -#define FUTEXFS_SUPER_MAGIC 0xBAD1DEA #define STACK_END_MAGIC 0x57AC6E9D +#define V9FS_MAGIC 0x01021997 + +#define BDEVFS_MAGIC 0x62646576 +#define BINFMTFS_MAGIC 0x42494e4d #define DEVPTS_SUPER_MAGIC 0x1cd1 +#define FUTEXFS_SUPER_MAGIC 0xBAD1DEA +#define PIPEFS_MAGIC 0x50495045 +#define PROC_SUPER_MAGIC 0x9fa0 #define SOCKFS_MAGIC 0x534F434B -#define V9FS_MAGIC 0x01021997 +#define SYSFS_MAGIC 0x62656572 +#define USBDEVICE_SUPER_MAGIC 0x9fa2 +#define MTD_INODE_FS_MAGIC 0x11307854 +#define ANON_INODE_FS_MAGIC 0x09041934 + #endif /* __LINUX_MAGIC_H__ */ diff --git a/include/linux/pipe_fs_i.h b/include/linux/pipe_fs_i.h index 77257c92155a..6d626ff0cfd0 100644 --- a/include/linux/pipe_fs_i.h +++ b/include/linux/pipe_fs_i.h @@ -1,8 +1,6 @@ #ifndef _LINUX_PIPE_FS_I_H #define _LINUX_PIPE_FS_I_H -#define PIPEFS_MAGIC 0x50495045 - #define PIPE_DEF_BUFFERS 16 #define PIPE_BUF_FLAG_LRU 0x01 /* page is on the LRU */ -- cgit v1.2.3 From d314d74c695f967e10598467a326f41c78ed1e20 Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Fri, 23 Mar 2012 15:01:51 -0700 Subject: nmi watchdog: do not use cpp symbol in Kconfig ARCH_HAS_NMI_WATCHDOG is a macro defined by arch, but config HARDLOCKUP_DETECTOR depends on it. This is wrong, ARCH_HAS_NMI_WATCHDOG has to be a Kconfig config, and arch's need it should select it explicitly. Signed-off-by: WANG Cong Acked-by: Don Zickus Acked-by: Mike Frysinger Cc: David Howells Cc: David Miller Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/Kconfig | 3 +++ arch/blackfin/Kconfig | 1 + arch/blackfin/include/asm/irq.h | 4 ---- arch/mn10300/Kconfig | 1 + arch/mn10300/include/asm/reset-regs.h | 4 ---- arch/sparc/Kconfig | 1 + arch/sparc/include/asm/irq_64.h | 1 - include/linux/nmi.h | 2 +- lib/Kconfig.debug | 2 +- 9 files changed, 8 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/arch/Kconfig b/arch/Kconfig index 5b448a74d0f7..a6f14f622d13 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -120,6 +120,9 @@ config HAVE_KRETPROBES config HAVE_OPTPROBES bool + +config HAVE_NMI_WATCHDOG + bool # # An arch should select this if it provides all these things: # diff --git a/arch/blackfin/Kconfig b/arch/blackfin/Kconfig index abe5a9e85148..c1269a1085e1 100644 --- a/arch/blackfin/Kconfig +++ b/arch/blackfin/Kconfig @@ -36,6 +36,7 @@ config BLACKFIN select GENERIC_ATOMIC64 select GENERIC_IRQ_PROBE select IRQ_PER_CPU if SMP + select HAVE_NMI_WATCHDOG if NMI_WATCHDOG config GENERIC_CSUM def_bool y diff --git a/arch/blackfin/include/asm/irq.h b/arch/blackfin/include/asm/irq.h index 12f4060a31b0..89de539ed010 100644 --- a/arch/blackfin/include/asm/irq.h +++ b/arch/blackfin/include/asm/irq.h @@ -38,8 +38,4 @@ #include -#ifdef CONFIG_NMI_WATCHDOG -# define ARCH_HAS_NMI_WATCHDOG -#endif - #endif /* _BFIN_IRQ_H_ */ diff --git a/arch/mn10300/Kconfig b/arch/mn10300/Kconfig index 8f1c40d5817e..3aa3de017159 100644 --- a/arch/mn10300/Kconfig +++ b/arch/mn10300/Kconfig @@ -5,6 +5,7 @@ config MN10300 select GENERIC_IRQ_SHOW select HAVE_ARCH_TRACEHOOK select HAVE_ARCH_KGDB + select HAVE_NMI_WATCHDOG if MN10300_WD_TIMER config AM33_2 def_bool n diff --git a/arch/mn10300/include/asm/reset-regs.h b/arch/mn10300/include/asm/reset-regs.h index 10c7502a113f..8ca2a42d365b 100644 --- a/arch/mn10300/include/asm/reset-regs.h +++ b/arch/mn10300/include/asm/reset-regs.h @@ -17,10 +17,6 @@ #ifdef __KERNEL__ -#ifdef CONFIG_MN10300_WD_TIMER -#define ARCH_HAS_NMI_WATCHDOG /* See include/linux/nmi.h */ -#endif - /* * watchdog timer registers */ diff --git a/arch/sparc/Kconfig b/arch/sparc/Kconfig index ca5580e4d813..1666de84d477 100644 --- a/arch/sparc/Kconfig +++ b/arch/sparc/Kconfig @@ -29,6 +29,7 @@ config SPARC select GENERIC_IRQ_SHOW select USE_GENERIC_SMP_HELPERS if SMP select GENERIC_PCI_IOMAP + select HAVE_NMI_WATCHDOG if SPARC64 config SPARC32 def_bool !64BIT diff --git a/arch/sparc/include/asm/irq_64.h b/arch/sparc/include/asm/irq_64.h index 16dcae6d56e7..abf6afe82ca8 100644 --- a/arch/sparc/include/asm/irq_64.h +++ b/arch/sparc/include/asm/irq_64.h @@ -95,7 +95,6 @@ void arch_trigger_all_cpu_backtrace(void); extern void *hardirq_stack[NR_CPUS]; extern void *softirq_stack[NR_CPUS]; #define __ARCH_HAS_DO_SOFTIRQ -#define ARCH_HAS_NMI_WATCHDOG #define NO_IRQ 0xffffffff diff --git a/include/linux/nmi.h b/include/linux/nmi.h index 2d304efc89df..db50840e6355 100644 --- a/include/linux/nmi.h +++ b/include/linux/nmi.h @@ -14,7 +14,7 @@ * may be used to reset the timeout - for code which intentionally * disables interrupts for a long time. This call is stateless. */ -#if defined(ARCH_HAS_NMI_WATCHDOG) || defined(CONFIG_HARDLOCKUP_DETECTOR) +#if defined(CONFIG_HAVE_NMI_WATCHDOG) || defined(CONFIG_HARDLOCKUP_DETECTOR) #include extern void touch_nmi_watchdog(void); #else diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index 05037dc9bde7..391003f7ab46 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -184,7 +184,7 @@ config LOCKUP_DETECTOR config HARDLOCKUP_DETECTOR def_bool LOCKUP_DETECTOR && PERF_EVENTS && HAVE_PERF_EVENTS_NMI && \ - !ARCH_HAS_NMI_WATCHDOG + !HAVE_NMI_WATCHDOG config BOOTPARAM_HARDLOCKUP_PANIC bool "Panic (Reboot) On Hard Lockups" -- cgit v1.2.3 From 10db4e1e4e9a910a26b94045660e5ba7e7c71419 Mon Sep 17 00:00:00 2001 From: Bobby Powers Date: Fri, 23 Mar 2012 15:01:51 -0700 Subject: headers: include linux/types.h where appropriate This addresses some header check warnings. DRM headers which include "drm.h" have been excluded, as they indirectly include types.h. Signed-off-by: Bobby Powers Cc: Chris Ball Cc: Dave Airlie Cc: James Bottomley Cc: Takashi Iwai Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/drm/drm_mode.h | 2 ++ include/linux/mmc/ioctl.h | 3 +++ include/scsi/scsi_netlink.h | 2 +- include/sound/compress_params.h | 2 ++ 4 files changed, 8 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/drm/drm_mode.h b/include/drm/drm_mode.h index 2a2acda8b437..4a0aae38e160 100644 --- a/include/drm/drm_mode.h +++ b/include/drm/drm_mode.h @@ -27,6 +27,8 @@ #ifndef _DRM_MODE_H #define _DRM_MODE_H +#include + #define DRM_DISPLAY_INFO_LEN 32 #define DRM_CONNECTOR_NAME_LEN 32 #define DRM_DISPLAY_MODE_LEN 32 diff --git a/include/linux/mmc/ioctl.h b/include/linux/mmc/ioctl.h index 8fa5bc5f8059..1f5e68923929 100644 --- a/include/linux/mmc/ioctl.h +++ b/include/linux/mmc/ioctl.h @@ -1,5 +1,8 @@ #ifndef LINUX_MMC_IOCTL_H #define LINUX_MMC_IOCTL_H + +#include + struct mmc_ioc_cmd { /* Implies direction of data. true = write, false = read */ int write_flag; diff --git a/include/scsi/scsi_netlink.h b/include/scsi/scsi_netlink.h index 58ce8fe44783..5cb20ccb1956 100644 --- a/include/scsi/scsi_netlink.h +++ b/include/scsi/scsi_netlink.h @@ -23,7 +23,7 @@ #define SCSI_NETLINK_H #include - +#include /* * This file intended to be included by both kernel and user space diff --git a/include/sound/compress_params.h b/include/sound/compress_params.h index d97d69f81a7d..da4a456de032 100644 --- a/include/sound/compress_params.h +++ b/include/sound/compress_params.h @@ -51,6 +51,8 @@ #ifndef __SND_COMPRESS_PARAMS_H #define __SND_COMPRESS_PARAMS_H +#include + /* AUDIO CODECS SUPPORTED */ #define MAX_NUM_CODECS 32 #define MAX_NUM_CODEC_DESCRIPTORS 32 -- cgit v1.2.3 From 7ccaba5314caf3a2b1052edb3146ccc969b4d466 Mon Sep 17 00:00:00 2001 From: Jan Beulich Date: Fri, 23 Mar 2012 15:01:52 -0700 Subject: consolidate WARN_...ONCE() static variables Due to the alignment of following variables, these typically consume more than just the single byte that 'bool' requires, and as there are a few hundred instances, the cache pollution (not so much the waste of memory) sums up. Put these variables into their own section, outside of any half way frequently used memory range. Do the same also to the __warned variable of rcu_lockdep_assert(). (Don't, however, include the ones used by printk_once() and alike, as they can potentially be hot.) Signed-off-by: Jan Beulich Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/asm-generic/bug.h | 6 +++--- include/asm-generic/vmlinux.lds.h | 1 + include/linux/rcupdate.h | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/include/asm-generic/bug.h b/include/asm-generic/bug.h index 84458b0c38d1..2520a6e241dc 100644 --- a/include/asm-generic/bug.h +++ b/include/asm-generic/bug.h @@ -134,7 +134,7 @@ extern void warn_slowpath_null(const char *file, const int line); #endif #define WARN_ON_ONCE(condition) ({ \ - static bool __warned; \ + static bool __section(.data.unlikely) __warned; \ int __ret_warn_once = !!(condition); \ \ if (unlikely(__ret_warn_once)) \ @@ -144,7 +144,7 @@ extern void warn_slowpath_null(const char *file, const int line); }) #define WARN_ONCE(condition, format...) ({ \ - static bool __warned; \ + static bool __section(.data.unlikely) __warned; \ int __ret_warn_once = !!(condition); \ \ if (unlikely(__ret_warn_once)) \ @@ -154,7 +154,7 @@ extern void warn_slowpath_null(const char *file, const int line); }) #define WARN_TAINT_ONCE(condition, taint, format...) ({ \ - static bool __warned; \ + static bool __section(.data.unlikely) __warned; \ int __ret_warn_once = !!(condition); \ \ if (unlikely(__ret_warn_once)) \ diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index b5e2e4c6b017..798603e8ec38 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -167,6 +167,7 @@ CPU_KEEP(exit.data) \ MEM_KEEP(init.data) \ MEM_KEEP(exit.data) \ + *(.data.unlikely) \ STRUCT_ALIGN(); \ *(__tracepoints) \ /* implement dynamic printk debug */ \ diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h index 937217425c47..2c62594b67dd 100644 --- a/include/linux/rcupdate.h +++ b/include/linux/rcupdate.h @@ -418,7 +418,7 @@ extern int rcu_my_thread_group_empty(void); */ #define rcu_lockdep_assert(c, s) \ do { \ - static bool __warned; \ + static bool __section(.data.unlikely) __warned; \ if (debug_lockdep_rcu_enabled() && !__warned && !(c)) { \ __warned = true; \ lockdep_rcu_suspicious(__FILE__, __LINE__, s); \ -- cgit v1.2.3 From ebec18a6d3aa1e7d84aab16225e87fd25170ec2b Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 23 Mar 2012 15:01:54 -0700 Subject: prctl: add PR_{SET,GET}_CHILD_SUBREAPER to allow simple process supervision Userspace service managers/supervisors need to track their started services. Many services daemonize by double-forking and get implicitly re-parented to PID 1. The service manager will no longer be able to receive the SIGCHLD signals for them, and is no longer in charge of reaping the children with wait(). All information about the children is lost at the moment PID 1 cleans up the re-parented processes. With this prctl, a service manager process can mark itself as a sort of 'sub-init', able to stay as the parent for all orphaned processes created by the started services. All SIGCHLD signals will be delivered to the service manager. Receiving SIGCHLD and doing wait() is in cases of a service-manager much preferred over any possible asynchronous notification about specific PIDs, because the service manager has full access to the child process data in /proc and the PID can not be re-used until the wait(), the service-manager itself is in charge of, has happened. As a side effect, the relevant parent PID information does not get lost by a double-fork, which results in a more elaborate process tree and 'ps' output: before: # ps afx 253 ? Ss 0:00 /bin/dbus-daemon --system --nofork 294 ? Sl 0:00 /usr/libexec/polkit-1/polkitd 328 ? S 0:00 /usr/sbin/modem-manager 608 ? Sl 0:00 /usr/libexec/colord 658 ? Sl 0:00 /usr/libexec/upowerd 819 ? Sl 0:00 /usr/libexec/imsettings-daemon 916 ? Sl 0:00 /usr/libexec/udisks-daemon 917 ? S 0:00 \_ udisks-daemon: not polling any devices after: # ps afx 294 ? Ss 0:00 /bin/dbus-daemon --system --nofork 426 ? Sl 0:00 \_ /usr/libexec/polkit-1/polkitd 449 ? S 0:00 \_ /usr/sbin/modem-manager 635 ? Sl 0:00 \_ /usr/libexec/colord 705 ? Sl 0:00 \_ /usr/libexec/upowerd 959 ? Sl 0:00 \_ /usr/libexec/udisks-daemon 960 ? S 0:00 | \_ udisks-daemon: not polling any devices 977 ? Sl 0:00 \_ /usr/libexec/packagekitd This prctl is orthogonal to PID namespaces. PID namespaces are isolated from each other, while a service management process usually requires the services to live in the same namespace, to be able to talk to each other. Users of this will be the systemd per-user instance, which provides init-like functionality for the user's login session and D-Bus, which activates bus services on-demand. Both need init-like capabilities to be able to properly keep track of the services they start. Many thanks to Oleg for several rounds of review and insights. [akpm@linux-foundation.org: fix comment layout and spelling] [akpm@linux-foundation.org: add lengthy code comment from Oleg] Reviewed-by: Oleg Nesterov Signed-off-by: Lennart Poettering Signed-off-by: Kay Sievers Acked-by: Valdis Kletnieks Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/prctl.h | 3 +++ include/linux/sched.h | 12 ++++++++++++ kernel/exit.c | 33 ++++++++++++++++++++++++++++----- kernel/fork.c | 3 +++ kernel/sys.c | 8 ++++++++ 5 files changed, 54 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/include/linux/prctl.h b/include/linux/prctl.h index a0413ac3abe8..e0cfec2490aa 100644 --- a/include/linux/prctl.h +++ b/include/linux/prctl.h @@ -121,4 +121,7 @@ #define PR_SET_PTRACER 0x59616d61 # define PR_SET_PTRACER_ANY ((unsigned long)-1) +#define PR_SET_CHILD_SUBREAPER 36 +#define PR_GET_CHILD_SUBREAPER 37 + #endif /* _LINUX_PRCTL_H */ diff --git a/include/linux/sched.h b/include/linux/sched.h index 0c147a4260a5..0c3854b0d4b1 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -553,6 +553,18 @@ struct signal_struct { int group_stop_count; unsigned int flags; /* see SIGNAL_* flags below */ + /* + * PR_SET_CHILD_SUBREAPER marks a process, like a service + * manager, to re-parent orphan (double-forking) child processes + * to this process instead of 'init'. The service manager is + * able to receive SIGCHLD signals and is able to investigate + * the process until it calls wait(). All children of this + * process will inherit a flag if they should look for a + * child_subreaper process at exit. + */ + unsigned int is_child_subreaper:1; + unsigned int has_child_subreaper:1; + /* POSIX.1b Interval Timers */ struct list_head posix_timers; diff --git a/kernel/exit.c b/kernel/exit.c index 16b07bfac224..456329fd4ea3 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -687,11 +687,11 @@ static void exit_mm(struct task_struct * tsk) } /* - * When we die, we re-parent all our children. - * Try to give them to another thread in our thread - * group, and if no such member exists, give it to - * the child reaper process (ie "init") in our pid - * space. + * When we die, we re-parent all our children, and try to: + * 1. give them to another thread in our thread group, if such a member exists + * 2. give it to the first ancestor process which prctl'd itself as a + * child_subreaper for its children (like a service manager) + * 3. give it to the init process (PID 1) in our pid namespace */ static struct task_struct *find_new_reaper(struct task_struct *father) __releases(&tasklist_lock) @@ -722,6 +722,29 @@ static struct task_struct *find_new_reaper(struct task_struct *father) * forget_original_parent() must move them somewhere. */ pid_ns->child_reaper = init_pid_ns.child_reaper; + } else if (father->signal->has_child_subreaper) { + struct task_struct *reaper; + + /* + * Find the first ancestor marked as child_subreaper. + * Note that the code below checks same_thread_group(reaper, + * pid_ns->child_reaper). This is what we need to DTRT in a + * PID namespace. However we still need the check above, see + * http://marc.info/?l=linux-kernel&m=131385460420380 + */ + for (reaper = father->real_parent; + reaper != &init_task; + reaper = reaper->real_parent) { + if (same_thread_group(reaper, pid_ns->child_reaper)) + break; + if (!reaper->signal->is_child_subreaper) + continue; + thread = reaper; + do { + if (!(thread->flags & PF_EXITING)) + return reaper; + } while_each_thread(reaper, thread); + } } return pid_ns->child_reaper; diff --git a/kernel/fork.c b/kernel/fork.c index 37674ec55cde..b9372a0bff18 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -1051,6 +1051,9 @@ static int copy_signal(unsigned long clone_flags, struct task_struct *tsk) sig->oom_score_adj = current->signal->oom_score_adj; sig->oom_score_adj_min = current->signal->oom_score_adj_min; + sig->has_child_subreaper = current->signal->has_child_subreaper || + current->signal->is_child_subreaper; + mutex_init(&sig->cred_guard_mutex); return 0; diff --git a/kernel/sys.c b/kernel/sys.c index 888d227fd195..9eb7fcab8df6 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -1962,6 +1962,14 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3, case PR_SET_MM: error = prctl_set_mm(arg2, arg3, arg4, arg5); break; + case PR_SET_CHILD_SUBREAPER: + me->signal->is_child_subreaper = !!arg2; + error = 0; + break; + case PR_GET_CHILD_SUBREAPER: + error = put_user(me->signal->is_child_subreaper, + (int __user *) arg2); + break; default: error = -EINVAL; break; -- cgit v1.2.3 From 7be865ab8634d4ec2a6bdb9459b268cd60e832af Mon Sep 17 00:00:00 2001 From: "Kim, Milo" Date: Fri, 23 Mar 2012 15:02:01 -0700 Subject: backlight: new backlight driver for LP855x devices THis driver supports TI LP8550/LP8551/LP8552/LP8553/LP8556 backlight devices. The brightness can be controlled by the I2C or PWM input. The lp855x driver provides both modes. For the PWM control, pwm-specific functions can be defined in the platform data. And some information can be read via the sysfs(lp855x device attributes). For details, please refer to Documentation/backlight/lp855x-driver.txt. [axel.lin@gmail.com: add missing mutex_unlock in lp855x_read_byte() error path] [axel.lin@gmail.com: check platform data in lp855x_probe()] [axel.lin@gmail.com: small cleanups] [dan.carpenter@oracle.com: silence a compiler warning] [axel.lin@gmail.com: use id->driver_data to differentiate lp855x chips] [akpm@linux-foundation.org: simplify boolean return expression] Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Axel Lin Signed-off-by: Dan Carpenter Cc: Richard Purdie Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/backlight/lp855x-driver.txt | 78 +++++++ drivers/video/backlight/Kconfig | 7 + drivers/video/backlight/Makefile | 1 + drivers/video/backlight/lp855x_bl.c | 331 ++++++++++++++++++++++++++++++ include/linux/lp855x.h | 131 ++++++++++++ 5 files changed, 548 insertions(+) create mode 100644 Documentation/backlight/lp855x-driver.txt create mode 100644 drivers/video/backlight/lp855x_bl.c create mode 100644 include/linux/lp855x.h (limited to 'include/linux') diff --git a/Documentation/backlight/lp855x-driver.txt b/Documentation/backlight/lp855x-driver.txt new file mode 100644 index 000000000000..f5e4caafab7d --- /dev/null +++ b/Documentation/backlight/lp855x-driver.txt @@ -0,0 +1,78 @@ +Kernel driver lp855x +==================== + +Backlight driver for LP855x ICs + +Supported chips: + Texas Instruments LP8550, LP8551, LP8552, LP8553 and LP8556 + +Author: Milo(Woogyom) Kim + +Description +----------- + +* Brightness control + +Brightness can be controlled by the pwm input or the i2c command. +The lp855x driver supports both cases. + +* Device attributes + +1) bl_ctl_mode +Backlight control mode. +Value : pwm based or register based + +2) chip_id +The lp855x chip id. +Value : lp8550/lp8551/lp8552/lp8553/lp8556 + +Platform data for lp855x +------------------------ + +For supporting platform specific data, the lp855x platform data can be used. + +* name : Backlight driver name. If it is not defined, default name is set. +* mode : Brightness control mode. PWM or register based. +* device_control : Value of DEVICE CONTROL register. +* initial_brightness : Initial value of backlight brightness. +* pwm_data : Platform specific pwm generation functions. + Only valid when brightness is pwm input mode. + Functions should be implemented by PWM driver. + - pwm_set_intensity() : set duty of PWM + - pwm_get_intensity() : get current duty of PWM +* load_new_rom_data : + 0 : use default configuration data + 1 : update values of eeprom or eprom registers on loading driver +* size_program : Total size of lp855x_rom_data. +* rom_data : List of new eeprom/eprom registers. + +example 1) lp8552 platform data : i2c register mode with new eeprom data + +#define EEPROM_A5_ADDR 0xA5 +#define EEPROM_A5_VAL 0x4f /* EN_VSYNC=0 */ + +static struct lp855x_rom_data lp8552_eeprom_arr[] = { + {EEPROM_A5_ADDR, EEPROM_A5_VAL}, +}; + +static struct lp855x_platform_data lp8552_pdata = { + .name = "lcd-bl", + .mode = REGISTER_BASED, + .device_control = I2C_CONFIG(LP8552), + .initial_brightness = INITIAL_BRT, + .load_new_rom_data = 1, + .size_program = ARRAY_SIZE(lp8552_eeprom_arr), + .rom_data = lp8552_eeprom_arr, +}; + +example 2) lp8556 platform data : pwm input mode with default rom data + +static struct lp855x_platform_data lp8556_pdata = { + .mode = PWM_BASED, + .device_control = PWM_CONFIG(LP8556), + .initial_brightness = INITIAL_BRT, + .pwm_data = { + .pwm_set_intensity = platform_pwm_set_intensity, + .pwm_get_intensity = platform_pwm_get_intensity, + }, +}; diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig index 681b36929fe4..49e7d83f869f 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig @@ -334,6 +334,13 @@ config BACKLIGHT_AAT2870 If you have a AnalogicTech AAT2870 say Y to enable the backlight driver. +config BACKLIGHT_LP855X + tristate "Backlight driver for TI LP855X" + depends on BACKLIGHT_CLASS_DEVICE && I2C + help + This supports TI LP8550, LP8551, LP8552, LP8553 and LP8556 + backlight driver. + endif # BACKLIGHT_CLASS_DEVICE endif # BACKLIGHT_LCD_SUPPORT diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile index af5cf654ec7c..0ee06e0832bd 100644 --- a/drivers/video/backlight/Makefile +++ b/drivers/video/backlight/Makefile @@ -22,6 +22,7 @@ obj-$(CONFIG_BACKLIGHT_GENERIC) += generic_bl.o obj-$(CONFIG_BACKLIGHT_HP700) += jornada720_bl.o obj-$(CONFIG_BACKLIGHT_HP680) += hp680_bl.o obj-$(CONFIG_BACKLIGHT_LOCOMO) += locomolcd.o +obj-$(CONFIG_BACKLIGHT_LP855X) += lp855x_bl.o obj-$(CONFIG_BACKLIGHT_OMAP1) += omap1_bl.o obj-$(CONFIG_BACKLIGHT_PROGEAR) += progear_bl.o obj-$(CONFIG_BACKLIGHT_CARILLO_RANCH) += cr_bllcd.o diff --git a/drivers/video/backlight/lp855x_bl.c b/drivers/video/backlight/lp855x_bl.c new file mode 100644 index 000000000000..72a0e0c917cf --- /dev/null +++ b/drivers/video/backlight/lp855x_bl.c @@ -0,0 +1,331 @@ +/* + * TI LP855x Backlight Driver + * + * Copyright (C) 2011 Texas Instruments + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include + +/* Registers */ +#define BRIGHTNESS_CTRL (0x00) +#define DEVICE_CTRL (0x01) + +#define BUF_SIZE 20 +#define DEFAULT_BL_NAME "lcd-backlight" +#define MAX_BRIGHTNESS 255 + +struct lp855x { + const char *chipname; + enum lp855x_chip_id chip_id; + struct i2c_client *client; + struct backlight_device *bl; + struct device *dev; + struct mutex xfer_lock; + struct lp855x_platform_data *pdata; +}; + +static int lp855x_read_byte(struct lp855x *lp, u8 reg, u8 *data) +{ + int ret; + + mutex_lock(&lp->xfer_lock); + ret = i2c_smbus_read_byte_data(lp->client, reg); + if (ret < 0) { + mutex_unlock(&lp->xfer_lock); + dev_err(lp->dev, "failed to read 0x%.2x\n", reg); + return ret; + } + mutex_unlock(&lp->xfer_lock); + + *data = (u8)ret; + return 0; +} + +static int lp855x_write_byte(struct lp855x *lp, u8 reg, u8 data) +{ + int ret; + + mutex_lock(&lp->xfer_lock); + ret = i2c_smbus_write_byte_data(lp->client, reg, data); + mutex_unlock(&lp->xfer_lock); + + return ret; +} + +static bool lp855x_is_valid_rom_area(struct lp855x *lp, u8 addr) +{ + u8 start, end; + + switch (lp->chip_id) { + case LP8550: + case LP8551: + case LP8552: + case LP8553: + start = EEPROM_START; + end = EEPROM_END; + break; + case LP8556: + start = EPROM_START; + end = EPROM_END; + break; + default: + return false; + } + + return (addr >= start && addr <= end); +} + +static int lp855x_init_registers(struct lp855x *lp) +{ + u8 val, addr; + int i, ret; + struct lp855x_platform_data *pd = lp->pdata; + + val = pd->initial_brightness; + ret = lp855x_write_byte(lp, BRIGHTNESS_CTRL, val); + if (ret) + return ret; + + val = pd->device_control; + ret = lp855x_write_byte(lp, DEVICE_CTRL, val); + if (ret) + return ret; + + if (pd->load_new_rom_data && pd->size_program) { + for (i = 0; i < pd->size_program; i++) { + addr = pd->rom_data[i].addr; + val = pd->rom_data[i].val; + if (!lp855x_is_valid_rom_area(lp, addr)) + continue; + + ret = lp855x_write_byte(lp, addr, val); + if (ret) + return ret; + } + } + + return ret; +} + +static int lp855x_bl_update_status(struct backlight_device *bl) +{ + struct lp855x *lp = bl_get_data(bl); + enum lp855x_brightness_ctrl_mode mode = lp->pdata->mode; + + if (bl->props.state & BL_CORE_SUSPENDED) + bl->props.brightness = 0; + + if (mode == PWM_BASED) { + struct lp855x_pwm_data *pd = &lp->pdata->pwm_data; + int br = bl->props.brightness; + int max_br = bl->props.max_brightness; + + if (pd->pwm_set_intensity) + pd->pwm_set_intensity(br, max_br); + + } else if (mode == REGISTER_BASED) { + u8 val = bl->props.brightness; + lp855x_write_byte(lp, BRIGHTNESS_CTRL, val); + } + + return 0; +} + +static int lp855x_bl_get_brightness(struct backlight_device *bl) +{ + struct lp855x *lp = bl_get_data(bl); + enum lp855x_brightness_ctrl_mode mode = lp->pdata->mode; + + if (mode == PWM_BASED) { + struct lp855x_pwm_data *pd = &lp->pdata->pwm_data; + int max_br = bl->props.max_brightness; + + if (pd->pwm_get_intensity) + bl->props.brightness = pd->pwm_get_intensity(max_br); + + } else if (mode == REGISTER_BASED) { + u8 val = 0; + + lp855x_read_byte(lp, BRIGHTNESS_CTRL, &val); + bl->props.brightness = val; + } + + return bl->props.brightness; +} + +static const struct backlight_ops lp855x_bl_ops = { + .options = BL_CORE_SUSPENDRESUME, + .update_status = lp855x_bl_update_status, + .get_brightness = lp855x_bl_get_brightness, +}; + +static int lp855x_backlight_register(struct lp855x *lp) +{ + struct backlight_device *bl; + struct backlight_properties props; + struct lp855x_platform_data *pdata = lp->pdata; + char *name = pdata->name ? : DEFAULT_BL_NAME; + + props.type = BACKLIGHT_PLATFORM; + props.max_brightness = MAX_BRIGHTNESS; + + if (pdata->initial_brightness > props.max_brightness) + pdata->initial_brightness = props.max_brightness; + + props.brightness = pdata->initial_brightness; + + bl = backlight_device_register(name, lp->dev, lp, + &lp855x_bl_ops, &props); + if (IS_ERR(bl)) + return PTR_ERR(bl); + + lp->bl = bl; + + return 0; +} + +static void lp855x_backlight_unregister(struct lp855x *lp) +{ + if (lp->bl) + backlight_device_unregister(lp->bl); +} + +static ssize_t lp855x_get_chip_id(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct lp855x *lp = dev_get_drvdata(dev); + return scnprintf(buf, BUF_SIZE, "%s\n", lp->chipname); +} + +static ssize_t lp855x_get_bl_ctl_mode(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct lp855x *lp = dev_get_drvdata(dev); + enum lp855x_brightness_ctrl_mode mode = lp->pdata->mode; + char *strmode = NULL; + + if (mode == PWM_BASED) + strmode = "pwm based"; + else if (mode == REGISTER_BASED) + strmode = "register based"; + + return scnprintf(buf, BUF_SIZE, "%s\n", strmode); +} + +static DEVICE_ATTR(chip_id, S_IRUGO, lp855x_get_chip_id, NULL); +static DEVICE_ATTR(bl_ctl_mode, S_IRUGO, lp855x_get_bl_ctl_mode, NULL); + +static struct attribute *lp855x_attributes[] = { + &dev_attr_chip_id.attr, + &dev_attr_bl_ctl_mode.attr, + NULL, +}; + +static const struct attribute_group lp855x_attr_group = { + .attrs = lp855x_attributes, +}; + +static int lp855x_probe(struct i2c_client *cl, const struct i2c_device_id *id) +{ + struct lp855x *lp; + struct lp855x_platform_data *pdata = cl->dev.platform_data; + enum lp855x_brightness_ctrl_mode mode; + int ret; + + if (!pdata) { + dev_err(&cl->dev, "no platform data supplied\n"); + return -EINVAL; + } + + if (!i2c_check_functionality(cl->adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) + return -EIO; + + lp = devm_kzalloc(&cl->dev, sizeof(struct lp855x), GFP_KERNEL); + if (!lp) + return -ENOMEM; + + mode = pdata->mode; + lp->client = cl; + lp->dev = &cl->dev; + lp->pdata = pdata; + lp->chipname = id->name; + lp->chip_id = id->driver_data; + i2c_set_clientdata(cl, lp); + + mutex_init(&lp->xfer_lock); + + ret = lp855x_init_registers(lp); + if (ret) { + dev_err(lp->dev, "i2c communication err: %d", ret); + if (mode == REGISTER_BASED) + goto err_dev; + } + + ret = lp855x_backlight_register(lp); + if (ret) { + dev_err(lp->dev, + "failed to register backlight. err: %d\n", ret); + goto err_dev; + } + + ret = sysfs_create_group(&lp->dev->kobj, &lp855x_attr_group); + if (ret) { + dev_err(lp->dev, "failed to register sysfs. err: %d\n", ret); + goto err_sysfs; + } + + backlight_update_status(lp->bl); + return 0; + +err_sysfs: + lp855x_backlight_unregister(lp); +err_dev: + return ret; +} + +static int __devexit lp855x_remove(struct i2c_client *cl) +{ + struct lp855x *lp = i2c_get_clientdata(cl); + + lp->bl->props.brightness = 0; + backlight_update_status(lp->bl); + sysfs_remove_group(&lp->dev->kobj, &lp855x_attr_group); + lp855x_backlight_unregister(lp); + + return 0; +} + +static const struct i2c_device_id lp855x_ids[] = { + {"lp8550", LP8550}, + {"lp8551", LP8551}, + {"lp8552", LP8552}, + {"lp8553", LP8553}, + {"lp8556", LP8556}, + { } +}; +MODULE_DEVICE_TABLE(i2c, lp855x_ids); + +static struct i2c_driver lp855x_driver = { + .driver = { + .name = "lp855x", + }, + .probe = lp855x_probe, + .remove = __devexit_p(lp855x_remove), + .id_table = lp855x_ids, +}; + +module_i2c_driver(lp855x_driver); + +MODULE_DESCRIPTION("Texas Instruments LP855x Backlight driver"); +MODULE_AUTHOR("Milo Kim "); +MODULE_LICENSE("GPL"); diff --git a/include/linux/lp855x.h b/include/linux/lp855x.h new file mode 100644 index 000000000000..781a490a451b --- /dev/null +++ b/include/linux/lp855x.h @@ -0,0 +1,131 @@ +/* + * LP855x Backlight Driver + * + * Copyright (C) 2011 Texas Instruments + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#ifndef _LP855X_H +#define _LP855X_H + +#define BL_CTL_SHFT (0) +#define BRT_MODE_SHFT (1) +#define BRT_MODE_MASK (0x06) + +/* Enable backlight. Only valid when BRT_MODE=10(I2C only) */ +#define ENABLE_BL (1) +#define DISABLE_BL (0) + +#define I2C_CONFIG(id) id ## _I2C_CONFIG +#define PWM_CONFIG(id) id ## _PWM_CONFIG + +/* DEVICE CONTROL register - LP8550 */ +#define LP8550_PWM_CONFIG (LP8550_PWM_ONLY << BRT_MODE_SHFT) +#define LP8550_I2C_CONFIG ((ENABLE_BL << BL_CTL_SHFT) | \ + (LP8550_I2C_ONLY << BRT_MODE_SHFT)) + +/* DEVICE CONTROL register - LP8551 */ +#define LP8551_PWM_CONFIG LP8550_PWM_CONFIG +#define LP8551_I2C_CONFIG LP8550_I2C_CONFIG + +/* DEVICE CONTROL register - LP8552 */ +#define LP8552_PWM_CONFIG LP8550_PWM_CONFIG +#define LP8552_I2C_CONFIG LP8550_I2C_CONFIG + +/* DEVICE CONTROL register - LP8553 */ +#define LP8553_PWM_CONFIG LP8550_PWM_CONFIG +#define LP8553_I2C_CONFIG LP8550_I2C_CONFIG + +/* DEVICE CONTROL register - LP8556 */ +#define LP8556_PWM_CONFIG (LP8556_PWM_ONLY << BRT_MODE_SHFT) +#define LP8556_COMB1_CONFIG (LP8556_COMBINED1 << BRT_MODE_SHFT) +#define LP8556_I2C_CONFIG ((ENABLE_BL << BL_CTL_SHFT) | \ + (LP8556_I2C_ONLY << BRT_MODE_SHFT)) +#define LP8556_COMB2_CONFIG (LP8556_COMBINED2 << BRT_MODE_SHFT) + +/* ROM area boundary */ +#define EEPROM_START (0xA0) +#define EEPROM_END (0xA7) +#define EPROM_START (0xA0) +#define EPROM_END (0xAF) + +enum lp855x_chip_id { + LP8550, + LP8551, + LP8552, + LP8553, + LP8556, +}; + +enum lp855x_brightness_ctrl_mode { + PWM_BASED = 1, + REGISTER_BASED, +}; + +enum lp8550_brighntess_source { + LP8550_PWM_ONLY, + LP8550_I2C_ONLY = 2, +}; + +enum lp8551_brighntess_source { + LP8551_PWM_ONLY = LP8550_PWM_ONLY, + LP8551_I2C_ONLY = LP8550_I2C_ONLY, +}; + +enum lp8552_brighntess_source { + LP8552_PWM_ONLY = LP8550_PWM_ONLY, + LP8552_I2C_ONLY = LP8550_I2C_ONLY, +}; + +enum lp8553_brighntess_source { + LP8553_PWM_ONLY = LP8550_PWM_ONLY, + LP8553_I2C_ONLY = LP8550_I2C_ONLY, +}; + +enum lp8556_brightness_source { + LP8556_PWM_ONLY, + LP8556_COMBINED1, /* pwm + i2c before the shaper block */ + LP8556_I2C_ONLY, + LP8556_COMBINED2, /* pwm + i2c after the shaper block */ +}; + +struct lp855x_pwm_data { + void (*pwm_set_intensity) (int brightness, int max_brightness); + int (*pwm_get_intensity) (int max_brightness); +}; + +struct lp855x_rom_data { + u8 addr; + u8 val; +}; + +/** + * struct lp855x_platform_data + * @name : Backlight driver name. If it is not defined, default name is set. + * @mode : brightness control by pwm or lp855x register + * @device_control : value of DEVICE CONTROL register + * @initial_brightness : initial value of backlight brightness + * @pwm_data : platform specific pwm generation functions. + Only valid when mode is PWM_BASED. + * @load_new_rom_data : + 0 : use default configuration data + 1 : update values of eeprom or eprom registers on loading driver + * @size_program : total size of lp855x_rom_data + * @rom_data : list of new eeprom/eprom registers + */ +struct lp855x_platform_data { + char *name; + enum lp855x_brightness_ctrl_mode mode; + u8 device_control; + int initial_brightness; + struct lp855x_pwm_data pwm_data; + u8 load_new_rom_data; + int size_program; + struct lp855x_rom_data *rom_data; +}; + +#endif -- cgit v1.2.3 From 307b1cd7ecd7f3dc5ce3d3860957f034f0abe4df Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Fri, 23 Mar 2012 15:02:03 -0700 Subject: bitops: rename for_each_set_bit_cont() in favor of analogous list.h function This renames for_each_set_bit_cont() to for_each_set_bit_from() because it is analogous to list_for_each_entry_from() in list.h rather than list_for_each_entry_continue(). This doesn't remove for_each_set_bit_cont() for now. Signed-off-by: Akinobu Mita Cc: Robert Richter Cc: Thomas Gleixner Cc: Ingo Molnar Cc: "H. Peter Anvin" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/x86/kernel/cpu/perf_event.c | 4 ++-- include/linux/bitops.h | 5 ++++- tools/perf/util/include/linux/bitops.h | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index 0a18d16cb58d..fa2900c0e398 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c @@ -643,14 +643,14 @@ static bool __perf_sched_find_counter(struct perf_sched *sched) /* Prefer fixed purpose counters */ if (x86_pmu.num_counters_fixed) { idx = X86_PMC_IDX_FIXED; - for_each_set_bit_cont(idx, c->idxmsk, X86_PMC_IDX_MAX) { + for_each_set_bit_from(idx, c->idxmsk, X86_PMC_IDX_MAX) { if (!__test_and_set_bit(idx, sched->state.used)) goto done; } } /* Grab the first unused counter starting with idx */ idx = sched->state.counter; - for_each_set_bit_cont(idx, c->idxmsk, X86_PMC_IDX_FIXED) { + for_each_set_bit_from(idx, c->idxmsk, X86_PMC_IDX_FIXED) { if (!__test_and_set_bit(idx, sched->state.used)) goto done; } diff --git a/include/linux/bitops.h b/include/linux/bitops.h index 94300fe46cce..a78e358f0c17 100644 --- a/include/linux/bitops.h +++ b/include/linux/bitops.h @@ -27,11 +27,14 @@ extern unsigned long __sw_hweight64(__u64 w); (bit) = find_next_bit((addr), (size), (bit) + 1)) /* same as for_each_set_bit() but use bit as value to start with */ -#define for_each_set_bit_cont(bit, addr, size) \ +#define for_each_set_bit_from(bit, addr, size) \ for ((bit) = find_next_bit((addr), (size), (bit)); \ (bit) < (size); \ (bit) = find_next_bit((addr), (size), (bit) + 1)) +#define for_each_set_bit_cont(bit, addr, size) \ + for_each_set_bit_from(bit, addr, size) + static __inline__ int get_bitmask_order(unsigned int count) { int order; diff --git a/tools/perf/util/include/linux/bitops.h b/tools/perf/util/include/linux/bitops.h index 62cdee78db7b..f1584833bd22 100644 --- a/tools/perf/util/include/linux/bitops.h +++ b/tools/perf/util/include/linux/bitops.h @@ -15,7 +15,7 @@ (bit) = find_next_bit((addr), (size), (bit) + 1)) /* same as for_each_set_bit() but use bit as value to start with */ -#define for_each_set_bit_cont(bit, addr, size) \ +#define for_each_set_bit_from(bit, addr, size) \ for ((bit) = find_next_bit((addr), (size), (bit)); \ (bit) < (size); \ (bit) = find_next_bit((addr), (size), (bit) + 1)) -- cgit v1.2.3 From 0a329d2d5a1dd75273597538cdc33512ee38855e Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Fri, 23 Mar 2012 15:02:04 -0700 Subject: bitops: remove for_each_set_bit_cont() Remove for_each_set_bit_cont() after confirming that no one uses for_each_set_bit_cont() anymore. [sfr@canb.auug.org.au: regmap: cope with bitops API change] Signed-off-by: Akinobu Mita Signed-off-by: Stephen Rothwell Cc: Robert Richter Cc: Thomas Gleixner Cc: Ingo Molnar Cc: "H. Peter Anvin" Cc: Mark Brown Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/base/regmap/regcache-lzo.c | 2 +- include/linux/bitops.h | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) (limited to 'include/linux') diff --git a/drivers/base/regmap/regcache-lzo.c b/drivers/base/regmap/regcache-lzo.c index 8d0061569326..77dc53272289 100644 --- a/drivers/base/regmap/regcache-lzo.c +++ b/drivers/base/regmap/regcache-lzo.c @@ -341,7 +341,7 @@ static int regcache_lzo_sync(struct regmap *map, unsigned int min, lzo_blocks = map->cache; i = min; - for_each_set_bit_cont(i, lzo_blocks[0]->sync_bmp, + for_each_set_bit_from(i, lzo_blocks[0]->sync_bmp, lzo_blocks[0]->sync_bmp_nbits) { if (i > max) continue; diff --git a/include/linux/bitops.h b/include/linux/bitops.h index a78e358f0c17..348b1dca477a 100644 --- a/include/linux/bitops.h +++ b/include/linux/bitops.h @@ -32,9 +32,6 @@ extern unsigned long __sw_hweight64(__u64 w); (bit) < (size); \ (bit) = find_next_bit((addr), (size), (bit) + 1)) -#define for_each_set_bit_cont(bit, addr, size) \ - for_each_set_bit_from(bit, addr, size) - static __inline__ int get_bitmask_order(unsigned int count) { int order; -- cgit v1.2.3 From 03f4a8226c2f9c14361f75848d1e93139bab90c4 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Fri, 23 Mar 2012 15:02:04 -0700 Subject: bitops: introduce for_each_clear_bit() Introduce for_each_clear_bit() and for_each_clear_bit_from(). They are similar to for_each_set_bit() and list_for_each_set_bit_from(), but they iterate over all the cleared bits in a memory region. Signed-off-by: Akinobu Mita Cc: Robert Richter Cc: Thomas Gleixner Cc: Ingo Molnar Cc: "H. Peter Anvin" Cc: David Woodhouse Cc: Martin Schwidefsky Cc: Heiko Carstens Cc: Stefano Panella Cc: David Vrabel Cc: Sergei Shtylyov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/bitops.h | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'include/linux') diff --git a/include/linux/bitops.h b/include/linux/bitops.h index 348b1dca477a..a3b6b82108b9 100644 --- a/include/linux/bitops.h +++ b/include/linux/bitops.h @@ -32,6 +32,17 @@ extern unsigned long __sw_hweight64(__u64 w); (bit) < (size); \ (bit) = find_next_bit((addr), (size), (bit) + 1)) +#define for_each_clear_bit(bit, addr, size) \ + for ((bit) = find_first_zero_bit((addr), (size)); \ + (bit) < (size); \ + (bit) = find_next_zero_bit((addr), (size), (bit) + 1)) + +/* same as for_each_clear_bit() but use bit as value to start with */ +#define for_each_clear_bit_from(bit, addr, size) \ + for ((bit) = find_next_zero_bit((addr), (size), (bit)); \ + (bit) < (size); \ + (bit) = find_next_zero_bit((addr), (size), (bit) + 1)) + static __inline__ int get_bitmask_order(unsigned int count) { int order; -- cgit v1.2.3 From 5ae4e8a77dc82afcfe8460168ec0b94f4b79a54a Mon Sep 17 00:00:00 2001 From: "Kim, Milo" Date: Fri, 23 Mar 2012 15:02:08 -0700 Subject: drivers/leds/leds-lp5521.c: add 'name' in the lp5521_led_config The name of each led channel can be configurable. For the compatibility, the name is set to default value(xx:channelN) when 'name' is not defined. Signed-off-by: Milo(Woogyom) Kim Acked-by: Linus Walleij Cc: Arun MURTHY Cc: Srinidhi Kasagar Cc: Richard Purdie Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/leds/leds-lp5521.txt | 6 ++++++ drivers/leds/leds-lp5521.c | 11 ++++++++--- include/linux/leds-lp5521.h | 1 + 3 files changed, 15 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/Documentation/leds/leds-lp5521.txt b/Documentation/leds/leds-lp5521.txt index c4d8d151e0fe..f48ab757d120 100644 --- a/Documentation/leds/leds-lp5521.txt +++ b/Documentation/leds/leds-lp5521.txt @@ -43,17 +43,23 @@ Format: 10x mA i.e 10 means 1.0 mA example platform data: Note: chan_nr can have values between 0 and 2. +The name of each channel can be configurable. +If the name field is not defined, the default name will be set to 'xxxx:channelN' +(XXXX : pdata->label or i2c client name, N : channel number) static struct lp5521_led_config lp5521_led_config[] = { { + .name = "red", .chan_nr = 0, .led_current = 50, .max_current = 130, }, { + .name = "green", .chan_nr = 1, .led_current = 0, .max_current = 130, }, { + .name = "blue", .chan_nr = 2, .led_current = 0, .max_current = 130, diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index c42c8f049565..59feecdfe3a8 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -620,10 +620,15 @@ static int __devinit lp5521_init_led(struct lp5521_led *led, return -EINVAL; } - snprintf(name, sizeof(name), "%s:channel%d", - pdata->label ?: client->name, chan); led->cdev.brightness_set = lp5521_set_brightness; - led->cdev.name = name; + if (pdata->led_config[chan].name) { + led->cdev.name = pdata->led_config[chan].name; + } else { + snprintf(name, sizeof(name), "%s:channel%d", + pdata->label ?: client->name, chan); + led->cdev.name = name; + } + res = led_classdev_register(dev, &led->cdev); if (res < 0) { dev_err(dev, "couldn't register led on channel %d\n", chan); diff --git a/include/linux/leds-lp5521.h b/include/linux/leds-lp5521.h index fd548d2a8775..e675b8d4c7bf 100644 --- a/include/linux/leds-lp5521.h +++ b/include/linux/leds-lp5521.h @@ -26,6 +26,7 @@ /* See Documentation/leds/leds-lp5521.txt */ struct lp5521_led_config { + char *name; u8 chan_nr; u8 led_current; /* mA x10, 0 if led is not connected */ u8 max_current; -- cgit v1.2.3 From 3b49aacd0e56d5bf1b511f6554f17cd65eb8da64 Mon Sep 17 00:00:00 2001 From: "Kim, Milo" Date: Fri, 23 Mar 2012 15:02:08 -0700 Subject: drivers/leds/leds-lp5521.c: add 'update_config' in the lp5521_platform_data The value of CONFIG register(Addr 08h) is configurable. For supporting this feature, update_config is added in the platform data. If 'update_config' is not defined, the default value is 'LP5521_PWRSAVE_EN | LP5521_CP_MODE_AUTO | LP5521_R_TO_BATT'. To define CONFIG register in the platform data, the bit definitions were mo= ved to the header file. Documentation updated : description about 'update_config' and example. Signed-off-by: Milo(Woogyom) Kim Acked-by: Linus Walleij Cc: Arun MURTHY Cc: Srinidhi Kasagar Cc: Richard Purdie Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/leds/leds-lp5521.txt | 19 +++++++++++++++++++ drivers/leds/leds-lp5521.c | 19 ++++--------------- include/linux/leds-lp5521.h | 13 +++++++++++++ 3 files changed, 36 insertions(+), 15 deletions(-) (limited to 'include/linux') diff --git a/Documentation/leds/leds-lp5521.txt b/Documentation/leds/leds-lp5521.txt index f48ab757d120..e3c66c64591d 100644 --- a/Documentation/leds/leds-lp5521.txt +++ b/Documentation/leds/leds-lp5521.txt @@ -92,3 +92,22 @@ static struct lp5521_platform_data lp5521_platform_data = { If the current is set to 0 in the platform data, that channel is disabled and it is not visible in the sysfs. + +The 'update_config' : CONFIG register (ADDR 08h) +This value is platform-specific data. +If update_config is not defined, the CONFIG register is set with +'LP5521_PWRSAVE_EN | LP5521_CP_MODE_AUTO | LP5521_R_TO_BATT'. +(Enable auto-powersave, set charge pump to auto, red to battery) + +example of update_config : + +#define LP5521_CONFIGS (LP5521_PWM_HF | LP5521_PWRSAVE_EN | \ + LP5521_CP_MODE_AUTO | LP5521_R_TO_BATT | \ + LP5521_CLK_INT) + +static struct lp5521_platform_data lp5521_pdata = { + .led_config = lp5521_led_config, + .num_channels = ARRAY_SIZE(lp5521_led_config), + .clock_mode = LP5521_CLOCK_INT, + .update_config = LP5521_CONFIGS, +}; diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index 59feecdfe3a8..9682ece16011 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -82,18 +82,6 @@ #define LP5521_LOGARITHMIC_PWM 0x80 /* Logarithmic PWM adjustment */ #define LP5521_EXEC_RUN 0x2A -/* Bits in CONFIG register */ -#define LP5521_PWM_HF 0x40 /* PWM: 0 = 256Hz, 1 = 558Hz */ -#define LP5521_PWRSAVE_EN 0x20 /* 1 = Power save mode */ -#define LP5521_CP_MODE_OFF 0 /* Charge pump (CP) off */ -#define LP5521_CP_MODE_BYPASS 8 /* CP forced to bypass mode */ -#define LP5521_CP_MODE_1X5 0x10 /* CP forced to 1.5x mode */ -#define LP5521_CP_MODE_AUTO 0x18 /* Automatic mode selection */ -#define LP5521_R_TO_BATT 4 /* R out: 0 = CP, 1 = Vbat */ -#define LP5521_CLK_SRC_EXT 0 /* Ext-clk source (CLK_32K) */ -#define LP5521_CLK_INT 1 /* Internal clock */ -#define LP5521_CLK_AUTO 2 /* Automatic clock selection */ - /* Status */ #define LP5521_EXT_CLK_USED 0x08 @@ -241,15 +229,16 @@ static int lp5521_configure(struct i2c_client *client) { struct lp5521_chip *chip = i2c_get_clientdata(client); int ret; + u8 cfg; lp5521_init_engine(chip); /* Set all PWMs to direct control mode */ ret = lp5521_write(client, LP5521_REG_OP_MODE, 0x3F); - /* Enable auto-powersave, set charge pump to auto, red to battery */ - ret |= lp5521_write(client, LP5521_REG_CONFIG, - LP5521_PWRSAVE_EN | LP5521_CP_MODE_AUTO | LP5521_R_TO_BATT); + cfg = chip->pdata->update_config ? + : (LP5521_PWRSAVE_EN | LP5521_CP_MODE_AUTO | LP5521_R_TO_BATT); + ret |= lp5521_write(client, LP5521_REG_CONFIG, cfg); /* Initialize all channels PWM to zero -> leds off */ ret |= lp5521_write(client, LP5521_REG_R_PWM, 0); diff --git a/include/linux/leds-lp5521.h b/include/linux/leds-lp5521.h index e675b8d4c7bf..e9ab583cac36 100644 --- a/include/linux/leds-lp5521.h +++ b/include/linux/leds-lp5521.h @@ -36,6 +36,18 @@ struct lp5521_led_config { #define LP5521_CLOCK_INT 1 #define LP5521_CLOCK_EXT 2 +/* Bits in CONFIG register */ +#define LP5521_PWM_HF 0x40 /* PWM: 0 = 256Hz, 1 = 558Hz */ +#define LP5521_PWRSAVE_EN 0x20 /* 1 = Power save mode */ +#define LP5521_CP_MODE_OFF 0 /* Charge pump (CP) off */ +#define LP5521_CP_MODE_BYPASS 8 /* CP forced to bypass mode */ +#define LP5521_CP_MODE_1X5 0x10 /* CP forced to 1.5x mode */ +#define LP5521_CP_MODE_AUTO 0x18 /* Automatic mode selection */ +#define LP5521_R_TO_BATT 4 /* R out: 0 = CP, 1 = Vbat */ +#define LP5521_CLK_SRC_EXT 0 /* Ext-clk source (CLK_32K) */ +#define LP5521_CLK_INT 1 /* Internal clock */ +#define LP5521_CLK_AUTO 2 /* Automatic clock selection */ + struct lp5521_platform_data { struct lp5521_led_config *led_config; u8 num_channels; @@ -44,6 +56,7 @@ struct lp5521_platform_data { void (*release_resources)(void); void (*enable)(bool state); const char *label; + u8 update_config; }; #endif /* __LINUX_LP5521_H */ -- cgit v1.2.3 From 011af7bc7cd188a0310e2d26cdc2cc5d90148b0c Mon Sep 17 00:00:00 2001 From: "Kim, Milo" Date: Fri, 23 Mar 2012 15:02:09 -0700 Subject: drivers/leds/leds-lp5521.c: support led pattern data The lp5521 has autonomous operation mode without external control. Using lp5521_platform_data, various led patterns can be configurable. For supporting this feature, new functions and device attribute are added. Structure of lp5521_led_pattern: 3 channels are supported - red, green and blue. Pattern(s) of each channel and numbers of pattern(s) are defined in the pla= tform data. Pattern data are hexa codes which include pattern commands such like set pwm, wait, ramp up/down, branch and so on. Pattern mode functions: * lp5521_clear_program_memory Before running new led pattern, program memory should be cleared. * lp5521_write_program_memory Pattern data updated in the program memory via the i2c. * lp5521_get_pattern Get pattern from predefined in the platform data. * lp5521_run_led_pattern Stop current pattern or run new pattern. Transition time is required between different operation mode. Device attribute - 'led_pattern': To load specific led pattern, new device attribute is added. When the lp5521 driver is unloaded, stop current led pattern mode. Documentation updated : description about how to define the led patterns and example. [akpm@linux-foundation.org: checkpatch fixes] Signed-off-by: Milo(Woogyom) Kim Acked-by: Linus Walleij Cc: Arun MURTHY Cc: Srinidhi Kasagar Cc: Richard Purdie Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/leds/leds-lp5521.txt | 38 ++++++++++++++ drivers/leds/leds-lp5521.c | 102 ++++++++++++++++++++++++++++++++++++- include/linux/leds-lp5521.h | 11 ++++ 3 files changed, 150 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/Documentation/leds/leds-lp5521.txt b/Documentation/leds/leds-lp5521.txt index e3c66c64591d..0e542ab3d4a0 100644 --- a/Documentation/leds/leds-lp5521.txt +++ b/Documentation/leds/leds-lp5521.txt @@ -111,3 +111,41 @@ static struct lp5521_platform_data lp5521_pdata = { .clock_mode = LP5521_CLOCK_INT, .update_config = LP5521_CONFIGS, }; + +LED patterns : LP5521 has autonomous operation without external control. +Pattern data can be defined in the platform data. + +example of led pattern data : + +/* RGB(50,5,0) 500ms on, 500ms off, infinite loop */ +static u8 pattern_red[] = { + 0x40, 0x32, 0x60, 0x00, 0x40, 0x00, 0x60, 0x00, + }; + +static u8 pattern_green[] = { + 0x40, 0x05, 0x60, 0x00, 0x40, 0x00, 0x60, 0x00, + }; + +static struct lp5521_led_pattern board_led_patterns[] = { + { + .r = pattern_red, + .g = pattern_green, + .size_r = ARRAY_SIZE(pattern_red), + .size_g = ARRAY_SIZE(pattern_green), + }, +}; + +static struct lp5521_platform_data lp5521_platform_data = { + .led_config = lp5521_led_config, + .num_channels = ARRAY_SIZE(lp5521_led_config), + .clock_mode = LP5521_CLOCK_EXT, + .patterns = board_led_patterns, + .num_patterns = ARRAY_SIZE(board_led_patterns), +}; + +Then predefined led pattern(s) can be executed via the sysfs. +To start the pattern #1, +# echo 1 > /sys/bus/i2c/devices/xxxx/led_pattern +(xxxx : i2c bus & slave address) +To end the pattern, +# echo 0 > /sys/bus/i2c/devices/xxxx/led_pattern diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index 9682ece16011..007c7c921e7e 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -88,6 +88,9 @@ /* default R channel current register value */ #define LP5521_REG_R_CURR_DEFAULT 0xAF +/* Pattern Mode */ +#define PATTERN_OFF 0 + struct lp5521_engine { int id; u8 mode; @@ -493,7 +496,7 @@ static ssize_t store_current(struct device *dev, ssize_t ret; unsigned long curr; - if (strict_strtoul(buf, 0, &curr)) + if (kstrtoul(buf, 0, &curr)) return -EINVAL; if (curr > led->max_current) @@ -525,6 +528,100 @@ static ssize_t lp5521_selftest(struct device *dev, return sprintf(buf, "%s\n", ret ? "FAIL" : "OK"); } +static void lp5521_clear_program_memory(struct i2c_client *cl) +{ + int i; + u8 rgb_mem[] = { + LP5521_REG_R_PROG_MEM, + LP5521_REG_G_PROG_MEM, + LP5521_REG_B_PROG_MEM, + }; + + for (i = 0; i < ARRAY_SIZE(rgb_mem); i++) { + lp5521_write(cl, rgb_mem[i], 0); + lp5521_write(cl, rgb_mem[i] + 1, 0); + } +} + +static void lp5521_write_program_memory(struct i2c_client *cl, + u8 base, u8 *rgb, int size) +{ + int i; + + if (!rgb || size <= 0) + return; + + for (i = 0; i < size; i++) + lp5521_write(cl, base + i, *(rgb + i)); + + lp5521_write(cl, base + i, 0); + lp5521_write(cl, base + i + 1, 0); +} + +static inline struct lp5521_led_pattern *lp5521_get_pattern + (struct lp5521_chip *chip, u8 offset) +{ + struct lp5521_led_pattern *ptn; + ptn = chip->pdata->patterns + (offset - 1); + return ptn; +} + +static void lp5521_run_led_pattern(int mode, struct lp5521_chip *chip) +{ + struct lp5521_led_pattern *ptn; + struct i2c_client *cl = chip->client; + int num_patterns = chip->pdata->num_patterns; + + if (mode > num_patterns || !(chip->pdata->patterns)) + return; + + if (mode == PATTERN_OFF) { + lp5521_write(cl, LP5521_REG_ENABLE, + LP5521_MASTER_ENABLE | LP5521_LOGARITHMIC_PWM); + usleep_range(1000, 2000); + lp5521_write(cl, LP5521_REG_OP_MODE, LP5521_CMD_DIRECT); + } else { + ptn = lp5521_get_pattern(chip, mode); + if (!ptn) + return; + + lp5521_write(cl, LP5521_REG_OP_MODE, LP5521_CMD_LOAD); + usleep_range(1000, 2000); + + lp5521_clear_program_memory(cl); + + lp5521_write_program_memory(cl, LP5521_REG_R_PROG_MEM, + ptn->r, ptn->size_r); + lp5521_write_program_memory(cl, LP5521_REG_G_PROG_MEM, + ptn->g, ptn->size_g); + lp5521_write_program_memory(cl, LP5521_REG_B_PROG_MEM, + ptn->b, ptn->size_b); + + lp5521_write(cl, LP5521_REG_OP_MODE, LP5521_CMD_RUN); + usleep_range(1000, 2000); + lp5521_write(cl, LP5521_REG_ENABLE, + LP5521_MASTER_ENABLE | LP5521_LOGARITHMIC_PWM | + LP5521_EXEC_RUN); + } +} + +static ssize_t store_led_pattern(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct lp5521_chip *chip = i2c_get_clientdata(to_i2c_client(dev)); + unsigned long val; + int ret; + + ret = strict_strtoul(buf, 16, &val); + if (ret) + return ret; + + lp5521_run_led_pattern(val, chip); + + return len; +} + /* led class device attributes */ static DEVICE_ATTR(led_current, S_IRUGO | S_IWUSR, show_current, store_current); static DEVICE_ATTR(max_current, S_IRUGO , show_max_current, NULL); @@ -550,6 +647,7 @@ static DEVICE_ATTR(engine1_load, S_IWUSR, NULL, store_engine1_load); static DEVICE_ATTR(engine2_load, S_IWUSR, NULL, store_engine2_load); static DEVICE_ATTR(engine3_load, S_IWUSR, NULL, store_engine3_load); static DEVICE_ATTR(selftest, S_IRUGO, lp5521_selftest, NULL); +static DEVICE_ATTR(led_pattern, S_IWUSR, NULL, store_led_pattern); static struct attribute *lp5521_attributes[] = { &dev_attr_engine1_mode.attr, @@ -559,6 +657,7 @@ static struct attribute *lp5521_attributes[] = { &dev_attr_engine1_load.attr, &dev_attr_engine2_load.attr, &dev_attr_engine3_load.attr, + &dev_attr_led_pattern.attr, NULL }; @@ -761,6 +860,7 @@ static int __devexit lp5521_remove(struct i2c_client *client) struct lp5521_chip *chip = i2c_get_clientdata(client); int i; + lp5521_run_led_pattern(PATTERN_OFF, chip); lp5521_unregister_sysfs(client); for (i = 0; i < chip->num_leds; i++) { diff --git a/include/linux/leds-lp5521.h b/include/linux/leds-lp5521.h index e9ab583cac36..3f071ec019b2 100644 --- a/include/linux/leds-lp5521.h +++ b/include/linux/leds-lp5521.h @@ -32,6 +32,15 @@ struct lp5521_led_config { u8 max_current; }; +struct lp5521_led_pattern { + u8 *r; + u8 *g; + u8 *b; + u8 size_r; + u8 size_g; + u8 size_b; +}; + #define LP5521_CLOCK_AUTO 0 #define LP5521_CLOCK_INT 1 #define LP5521_CLOCK_EXT 2 @@ -57,6 +66,8 @@ struct lp5521_platform_data { void (*enable)(bool state); const char *label; u8 update_config; + struct lp5521_led_pattern *patterns; + int num_patterns; }; #endif /* __LINUX_LP5521_H */ -- cgit v1.2.3 From bb982009d3850759d3f4a4c853f9c456c48b6c2d Mon Sep 17 00:00:00 2001 From: "Kim, Milo" Date: Fri, 23 Mar 2012 15:02:12 -0700 Subject: leds-lm3530: support pwm input mode * add 'struct lm3530_pwm_data' in the platform data The pwm data is the platform specific functions which generate the pwm. The pwm data is only valid when brightness is pwm input mode. Functions should be implemented by the pwm driver. pwm_set_intensity() : set duty of pwm. pwm_get_intensity() : get current the brightness. * brightness control by pwm If the control mode is pwm, then brightness is changed by the duty of pwm=. So pwm platform function should be called in lm3530_brightness_set(). * do not update brightness register when pwm input mode In pwm input mode, brightness register is not used. If any value is updated in this register, then the led will be off. * when input mode is changed, set duty of pwm to 0 if unnecessary. Signed-off-by: Milo(Woogyom) Kim Cc: Linus Walleij Cc: Richard Purdie Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/leds/leds-lm3530.c | 32 ++++++++++++++++++++++++-------- include/linux/led-lm3530.h | 9 +++++++++ 2 files changed, 33 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/drivers/leds/leds-lm3530.c b/drivers/leds/leds-lm3530.c index ce79523a4d10..a889311eead1 100644 --- a/drivers/leds/leds-lm3530.c +++ b/drivers/leds/leds-lm3530.c @@ -157,6 +157,7 @@ static int lm3530_init_registers(struct lm3530_data *drvdata) u32 als_vmin, als_vmax, als_vstep; struct lm3530_platform_data *pltfm = drvdata->pdata; struct i2c_client *client = drvdata->client; + struct lm3530_pwm_data *pwm = &pltfm->pwm_data; gen_config = (pltfm->brt_ramp_law << LM3530_RAMP_LAW_SHIFT) | ((pltfm->max_current & 7) << LM3530_MAX_CURR_SHIFT); @@ -240,6 +241,15 @@ static int lm3530_init_registers(struct lm3530_data *drvdata) } for (i = 0; i < LM3530_REG_MAX; i++) { + /* do not update brightness register when pwm mode */ + if (lm3530_reg[i] == LM3530_BRT_CTRL_REG && + drvdata->mode == LM3530_BL_MODE_PWM) { + if (pwm->pwm_set_intensity) + pwm->pwm_set_intensity(reg_val[i], + drvdata->led_dev.max_brightness); + continue; + } + ret = i2c_smbus_write_byte_data(client, lm3530_reg[i], reg_val[i]); if (ret) @@ -255,6 +265,9 @@ static void lm3530_brightness_set(struct led_classdev *led_cdev, int err; struct lm3530_data *drvdata = container_of(led_cdev, struct lm3530_data, led_dev); + struct lm3530_platform_data *pdata = drvdata->pdata; + struct lm3530_pwm_data *pwm = &pdata->pwm_data; + u8 max_brightness = led_cdev->max_brightness; switch (drvdata->mode) { case LM3530_BL_MODE_MANUAL: @@ -288,6 +301,8 @@ static void lm3530_brightness_set(struct led_classdev *led_cdev, case LM3530_BL_MODE_ALS: break; case LM3530_BL_MODE_PWM: + if (pwm->pwm_set_intensity) + pwm->pwm_set_intensity(brt_val, max_brightness); break; default: break; @@ -318,23 +333,24 @@ static ssize_t lm3530_mode_set(struct device *dev, struct device_attribute { struct led_classdev *led_cdev = dev_get_drvdata(dev); struct lm3530_data *drvdata; + struct lm3530_pwm_data *pwm; + u8 max_brightness; int mode, err; drvdata = container_of(led_cdev, struct lm3530_data, led_dev); + pwm = &drvdata->pdata->pwm_data; + max_brightness = led_cdev->max_brightness; mode = lm3530_get_mode_from_str(buf); if (mode < 0) { dev_err(dev, "Invalid mode\n"); return -EINVAL; } - if (mode == LM3530_BL_MODE_MANUAL) - drvdata->mode = LM3530_BL_MODE_MANUAL; - else if (mode == LM3530_BL_MODE_ALS) - drvdata->mode = LM3530_BL_MODE_ALS; - else if (mode == LM3530_BL_MODE_PWM) { - dev_err(dev, "PWM mode not supported\n"); - return -EINVAL; - } + drvdata->mode = mode; + + /* set pwm to low if unnecessary */ + if (mode != LM3530_BL_MODE_PWM && pwm->pwm_set_intensity) + pwm->pwm_set_intensity(0, max_brightness); err = lm3530_init_registers(drvdata); if (err) { diff --git a/include/linux/led-lm3530.h b/include/linux/led-lm3530.h index 8eb12357a110..eeae6e742471 100644 --- a/include/linux/led-lm3530.h +++ b/include/linux/led-lm3530.h @@ -72,6 +72,12 @@ enum lm3530_als_mode { LM3530_INPUT_CEIL, /* Max of ALS1 and ALS2 */ }; +/* PWM Platform Specific Data */ +struct lm3530_pwm_data { + void (*pwm_set_intensity) (int brightness, int max_brightness); + int (*pwm_get_intensity) (int max_brightness); +}; + /** * struct lm3530_platform_data * @mode: mode of operation i.e. Manual, ALS or PWM @@ -87,6 +93,7 @@ enum lm3530_als_mode { * @als_vmin: als input voltage calibrated for max brightness in mV * @als_vmax: als input voltage calibrated for min brightness in mV * @brt_val: brightness value (0-255) + * @pwm_data: PWM control functions (only valid when the mode is PWM) */ struct lm3530_platform_data { enum lm3530_mode mode; @@ -107,6 +114,8 @@ struct lm3530_platform_data { u32 als_vmax; u8 brt_val; + + struct lm3530_pwm_data pwm_data; }; #endif /* _LINUX_LED_LM3530_H__ */ -- cgit v1.2.3 From 6061d949dd984c762ee272a88e77699fa675d1c8 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Fri, 23 Mar 2012 15:02:16 -0700 Subject: include/ and checkpatch: prefer __scanf to __attribute__((format(scanf,...) It's equivalent to __printf, so prefer __scanf. Signed-off-by: Joe Perches Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/compiler-gcc.h | 3 ++- include/linux/kernel.h | 8 ++++---- include/xen/xenbus.h | 4 ++-- scripts/checkpatch.pl | 6 ++++++ 4 files changed, 14 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/include/linux/compiler-gcc.h b/include/linux/compiler-gcc.h index 3fd17c249221..e5834aa24b9e 100644 --- a/include/linux/compiler-gcc.h +++ b/include/linux/compiler-gcc.h @@ -87,7 +87,8 @@ */ #define __pure __attribute__((pure)) #define __aligned(x) __attribute__((aligned(x))) -#define __printf(a,b) __attribute__((format(printf,a,b))) +#define __printf(a, b) __attribute__((format(printf, a, b))) +#define __scanf(a, b) __attribute__((format(scanf, a, b))) #define noinline __attribute__((noinline)) #define __attribute_const__ __attribute__((__const__)) #define __maybe_unused __attribute__((unused)) diff --git a/include/linux/kernel.h b/include/linux/kernel.h index d801acb5e680..f2085b541a24 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -328,10 +328,10 @@ extern __printf(2, 3) char *kasprintf(gfp_t gfp, const char *fmt, ...); extern char *kvasprintf(gfp_t gfp, const char *fmt, va_list args); -extern int sscanf(const char *, const char *, ...) - __attribute__ ((format (scanf, 2, 3))); -extern int vsscanf(const char *, const char *, va_list) - __attribute__ ((format (scanf, 2, 0))); +extern __scanf(2, 3) +int sscanf(const char *, const char *, ...); +extern __scanf(2, 0) +int vsscanf(const char *, const char *, va_list); extern int get_option(char **str, int *pint); extern char *get_options(const char *str, int nints, int *ints); diff --git a/include/xen/xenbus.h b/include/xen/xenbus.h index e8c599b237c2..0a7515c1e3a4 100644 --- a/include/xen/xenbus.h +++ b/include/xen/xenbus.h @@ -139,9 +139,9 @@ int xenbus_transaction_start(struct xenbus_transaction *t); int xenbus_transaction_end(struct xenbus_transaction t, int abort); /* Single read and scanf: returns -errno or num scanned if > 0. */ +__scanf(4, 5) int xenbus_scanf(struct xenbus_transaction t, - const char *dir, const char *node, const char *fmt, ...) - __attribute__((format(scanf, 4, 5))); + const char *dir, const char *node, const char *fmt, ...); /* Single printf and write: returns -errno or 0. */ __printf(4, 5) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index a3b9782441f9..89d24b3ea438 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -3123,6 +3123,12 @@ sub process { "__printf(string-index, first-to-check) is preferred over __attribute__((format(printf, string-index, first-to-check)))\n" . $herecurr); } +# Check for __attribute__ format(scanf, prefer __scanf + if ($line =~ /\b__attribute__\s*\(\s*\(\s*format\s*\(\s*scanf\b/) { + WARN("PREFER_SCANF", + "__scanf(string-index, first-to-check) is preferred over __attribute__((format(scanf, string-index, first-to-check)))\n" . $herecurr); + } + # check for sizeof(&) if ($line =~ /\bsizeof\s*\(\s*\&/) { WARN("SIZEOF_ADDRESS", -- cgit v1.2.3 From 46c5801eaf86e83cb3a4142ad35188db5011fff0 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Fri, 23 Mar 2012 15:02:25 -0700 Subject: crc32: bolt on crc32c Reuse the existing crc32 code to stamp out a crc32c implementation. Signed-off-by: Darrick J. Wong Cc: Herbert Xu Cc: Bob Pearson Cc: Randy Dunlap Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/crc32.h | 2 ++ lib/Kconfig | 8 +++--- lib/crc32.c | 79 ++++++++++++++++++++++++++++++++++++--------------- lib/crc32defs.h | 7 +++++ lib/gen_crc32table.c | 35 ++++++++++++++++++----- 5 files changed, 97 insertions(+), 34 deletions(-) (limited to 'include/linux') diff --git a/include/linux/crc32.h b/include/linux/crc32.h index 391a259b2cc9..68267b64bb98 100644 --- a/include/linux/crc32.h +++ b/include/linux/crc32.h @@ -11,6 +11,8 @@ extern u32 crc32_le(u32 crc, unsigned char const *p, size_t len); extern u32 crc32_be(u32 crc, unsigned char const *p, size_t len); +extern u32 __crc32c_le(u32 crc, unsigned char const *p, size_t len); + #define crc32(seed, data, length) crc32_le(seed, (unsigned char const *)(data), length) /* diff --git a/lib/Kconfig b/lib/Kconfig index d5a86aa441ac..6d7ce4b138c4 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -61,14 +61,14 @@ config CRC_ITU_T functions require M here. config CRC32 - tristate "CRC32 functions" + tristate "CRC32/CRC32c functions" default y select BITREVERSE help This option is provided for the case where no in-kernel-tree - modules require CRC32 functions, but a module built outside the - kernel tree does. Such modules that use library CRC32 functions - require M here. + modules require CRC32/CRC32c functions, but a module built outside + the kernel tree does. Such modules that use library CRC32/CRC32c + functions require M here. config CRC32_SELFTEST bool "CRC32 perform self test on init" diff --git a/lib/crc32.c b/lib/crc32.c index a1a5145a93ba..87678dd1141e 100644 --- a/lib/crc32.c +++ b/lib/crc32.c @@ -46,7 +46,7 @@ #include "crc32table.h" MODULE_AUTHOR("Matt Domsch "); -MODULE_DESCRIPTION("Ethernet CRC32 calculations"); +MODULE_DESCRIPTION("Various CRC32 calculations"); MODULE_LICENSE("GPL"); #if CRC_LE_BITS > 8 || CRC_BE_BITS > 8 @@ -135,45 +135,66 @@ crc32_body(u32 crc, unsigned char const *buf, size_t len, const u32 (*tab)[256]) * @p: pointer to buffer over which CRC is run * @len: length of buffer @p */ -u32 __pure crc32_le(u32 crc, unsigned char const *p, size_t len) +static inline u32 __pure crc32_le_generic(u32 crc, unsigned char const *p, + size_t len, const u32 (*tab)[256], + u32 polynomial) { #if CRC_LE_BITS == 1 int i; while (len--) { crc ^= *p++; for (i = 0; i < 8; i++) - crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY_LE : 0); + crc = (crc >> 1) ^ ((crc & 1) ? polynomial : 0); } # elif CRC_LE_BITS == 2 while (len--) { crc ^= *p++; - crc = (crc >> 2) ^ crc32table_le[0][crc & 3]; - crc = (crc >> 2) ^ crc32table_le[0][crc & 3]; - crc = (crc >> 2) ^ crc32table_le[0][crc & 3]; - crc = (crc >> 2) ^ crc32table_le[0][crc & 3]; + crc = (crc >> 2) ^ tab[0][crc & 3]; + crc = (crc >> 2) ^ tab[0][crc & 3]; + crc = (crc >> 2) ^ tab[0][crc & 3]; + crc = (crc >> 2) ^ tab[0][crc & 3]; } # elif CRC_LE_BITS == 4 while (len--) { crc ^= *p++; - crc = (crc >> 4) ^ crc32table_le[0][crc & 15]; - crc = (crc >> 4) ^ crc32table_le[0][crc & 15]; + crc = (crc >> 4) ^ tab[0][crc & 15]; + crc = (crc >> 4) ^ tab[0][crc & 15]; } # elif CRC_LE_BITS == 8 /* aka Sarwate algorithm */ while (len--) { crc ^= *p++; - crc = (crc >> 8) ^ crc32table_le[0][crc & 255]; + crc = (crc >> 8) ^ tab[0][crc & 255]; } # else - const u32 (*tab)[] = crc32table_le; - crc = (__force u32) __cpu_to_le32(crc); crc = crc32_body(crc, p, len, tab); crc = __le32_to_cpu((__force __le32)crc); #endif return crc; } + +#if CRC_LE_BITS == 1 +u32 __pure crc32_le(u32 crc, unsigned char const *p, size_t len) +{ + return crc32_le_generic(crc, p, len, NULL, CRCPOLY_LE); +} +u32 __pure __crc32c_le(u32 crc, unsigned char const *p, size_t len) +{ + return crc32_le_generic(crc, p, len, NULL, CRC32C_POLY_LE); +} +#else +u32 __pure crc32_le(u32 crc, unsigned char const *p, size_t len) +{ + return crc32_le_generic(crc, p, len, crc32table_le, CRCPOLY_LE); +} +u32 __pure __crc32c_le(u32 crc, unsigned char const *p, size_t len) +{ + return crc32_le_generic(crc, p, len, crc32ctable_le, CRC32C_POLY_LE); +} +#endif EXPORT_SYMBOL(crc32_le); +EXPORT_SYMBOL(__crc32c_le); /** * crc32_be() - Calculate bitwise big-endian Ethernet AUTODIN II CRC32 @@ -182,7 +203,9 @@ EXPORT_SYMBOL(crc32_le); * @p: pointer to buffer over which CRC is run * @len: length of buffer @p */ -u32 __pure crc32_be(u32 crc, unsigned char const *p, size_t len) +static inline u32 __pure crc32_be_generic(u32 crc, unsigned char const *p, + size_t len, const u32 (*tab)[256], + u32 polynomial) { #if CRC_BE_BITS == 1 int i; @@ -190,37 +213,47 @@ u32 __pure crc32_be(u32 crc, unsigned char const *p, size_t len) crc ^= *p++ << 24; for (i = 0; i < 8; i++) crc = - (crc << 1) ^ ((crc & 0x80000000) ? CRCPOLY_BE : + (crc << 1) ^ ((crc & 0x80000000) ? polynomial : 0); } # elif CRC_BE_BITS == 2 while (len--) { crc ^= *p++ << 24; - crc = (crc << 2) ^ crc32table_be[0][crc >> 30]; - crc = (crc << 2) ^ crc32table_be[0][crc >> 30]; - crc = (crc << 2) ^ crc32table_be[0][crc >> 30]; - crc = (crc << 2) ^ crc32table_be[0][crc >> 30]; + crc = (crc << 2) ^ tab[0][crc >> 30]; + crc = (crc << 2) ^ tab[0][crc >> 30]; + crc = (crc << 2) ^ tab[0][crc >> 30]; + crc = (crc << 2) ^ tab[0][crc >> 30]; } # elif CRC_BE_BITS == 4 while (len--) { crc ^= *p++ << 24; - crc = (crc << 4) ^ crc32table_be[0][crc >> 28]; - crc = (crc << 4) ^ crc32table_be[0][crc >> 28]; + crc = (crc << 4) ^ tab[0][crc >> 28]; + crc = (crc << 4) ^ tab[0][crc >> 28]; } # elif CRC_BE_BITS == 8 while (len--) { crc ^= *p++ << 24; - crc = (crc << 8) ^ crc32table_be[0][crc >> 24]; + crc = (crc << 8) ^ tab[0][crc >> 24]; } # else - const u32 (*tab)[] = crc32table_be; - crc = (__force u32) __cpu_to_be32(crc); crc = crc32_body(crc, p, len, tab); crc = __be32_to_cpu((__force __be32)crc); # endif return crc; } + +#if CRC_LE_BITS == 1 +u32 __pure crc32_be(u32 crc, unsigned char const *p, size_t len) +{ + return crc32_be_generic(crc, p, len, NULL, CRCPOLY_BE); +} +#else +u32 __pure crc32_be(u32 crc, unsigned char const *p, size_t len) +{ + return crc32_be_generic(crc, p, len, crc32table_be, CRCPOLY_BE); +} +#endif EXPORT_SYMBOL(crc32_be); #ifdef CONFIG_CRC32_SELFTEST diff --git a/lib/crc32defs.h b/lib/crc32defs.h index 818159288489..6fd191732fec 100644 --- a/lib/crc32defs.h +++ b/lib/crc32defs.h @@ -6,6 +6,13 @@ #define CRCPOLY_LE 0xedb88320 #define CRCPOLY_BE 0x04c11db7 +/* + * This is the CRC32c polynomial, as outlined by Castagnoli. + * x^32+x^28+x^27+x^26+x^25+x^23+x^22+x^20+x^19+x^18+x^14+x^13+x^11+x^10+x^9+ + * x^8+x^6+x^0 + */ +#define CRC32C_POLY_LE 0x82F63B78 + /* * How many bits at a time to use. Valid values are 1, 2, 4, 8, 32 and 64. * For less performance-sensitive, use 4 or 8 to save table size. diff --git a/lib/gen_crc32table.c b/lib/gen_crc32table.c index 0d9edd17ee13..8f8d5439e2d9 100644 --- a/lib/gen_crc32table.c +++ b/lib/gen_crc32table.c @@ -23,6 +23,7 @@ static uint32_t crc32table_le[LE_TABLE_ROWS][256]; static uint32_t crc32table_be[BE_TABLE_ROWS][256]; +static uint32_t crc32ctable_le[LE_TABLE_ROWS][256]; /** * crc32init_le() - allocate and initialize LE table data @@ -31,27 +32,38 @@ static uint32_t crc32table_be[BE_TABLE_ROWS][256]; * fact that crctable[i^j] = crctable[i] ^ crctable[j]. * */ -static void crc32init_le(void) +static void crc32init_le_generic(const uint32_t polynomial, + uint32_t (*tab)[256]) { unsigned i, j; uint32_t crc = 1; - crc32table_le[0][0] = 0; + tab[0][0] = 0; for (i = LE_TABLE_SIZE >> 1; i; i >>= 1) { - crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY_LE : 0); + crc = (crc >> 1) ^ ((crc & 1) ? polynomial : 0); for (j = 0; j < LE_TABLE_SIZE; j += 2 * i) - crc32table_le[0][i + j] = crc ^ crc32table_le[0][j]; + tab[0][i + j] = crc ^ tab[0][j]; } for (i = 0; i < LE_TABLE_SIZE; i++) { - crc = crc32table_le[0][i]; + crc = tab[0][i]; for (j = 1; j < LE_TABLE_ROWS; j++) { - crc = crc32table_le[0][crc & 0xff] ^ (crc >> 8); - crc32table_le[j][i] = crc; + crc = tab[0][crc & 0xff] ^ (crc >> 8); + tab[j][i] = crc; } } } +static void crc32init_le(void) +{ + crc32init_le_generic(CRCPOLY_LE, crc32table_le); +} + +static void crc32cinit_le(void) +{ + crc32init_le_generic(CRC32C_POLY_LE, crc32ctable_le); +} + /** * crc32init_be() - allocate and initialize BE table data */ @@ -114,6 +126,15 @@ int main(int argc, char** argv) BE_TABLE_SIZE, "tobe"); printf("};\n"); } + if (CRC_LE_BITS > 1) { + crc32cinit_le(); + printf("static const u32 __cacheline_aligned " + "crc32ctable_le[%d][%d] = {", + LE_TABLE_ROWS, LE_TABLE_SIZE); + output_table(crc32ctable_le, LE_TABLE_ROWS, + LE_TABLE_SIZE, "tole"); + printf("};\n"); + } return 0; } -- cgit v1.2.3 From 626cf236608505d376e4799adb4f7eb00a8594af Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 23 Mar 2012 15:02:27 -0700 Subject: poll: add poll_requested_events() and poll_does_not_wait() functions In some cases the poll() implementation in a driver has to do different things depending on the events the caller wants to poll for. An example is when a driver needs to start a DMA engine if the caller polls for POLLIN, but doesn't want to do that if POLLIN is not requested but instead only POLLOUT or POLLPRI is requested. This is something that can happen in the video4linux subsystem among others. Unfortunately, the current epoll/poll/select implementation doesn't provide that information reliably. The poll_table_struct does have it: it has a key field with the event mask. But once a poll() call matches one or more bits of that mask any following poll() calls are passed a NULL poll_table pointer. Also, the eventpoll implementation always left the key field at ~0 instead of using the requested events mask. This was changed in eventpoll.c so the key field now contains the actual events that should be polled for as set by the caller. The solution to the NULL poll_table pointer is to set the qproc field to NULL in poll_table once poll() matches the events, not the poll_table pointer itself. That way drivers can obtain the mask through a new poll_requested_events inline. The poll_table_struct can still be NULL since some kernel code calls it internally (netfs_state_poll() in ./drivers/staging/pohmelfs/netfs.h). In that case poll_requested_events() returns ~0 (i.e. all events). Very rarely drivers might want to know whether poll_wait will actually wait. If another earlier file descriptor in the set already matched the events the caller wanted to wait for, then the kernel will return from the select() call without waiting. This might be useful information in order to avoid doing expensive work. A new helper function poll_does_not_wait() is added that drivers can use to detect this situation. This is now used in sock_poll_wait() in include/net/sock.h. This was the only place in the kernel that needed this information. Drivers should no longer access any of the poll_table internals, but use the poll_requested_events() and poll_does_not_wait() access functions instead. In order to enforce that the poll_table fields are now prepended with an underscore and a comment was added warning against using them directly. This required a change in unix_dgram_poll() in unix/af_unix.c which used the key field to get the requested events. It's been replaced by a call to poll_requested_events(). For qproc it was especially important to change its name since the behavior of that field changes with this patch since this function pointer can now be NULL when that wasn't possible in the past. Any driver accessing the qproc or key fields directly will now fail to compile. Some notes regarding the correctness of this patch: the driver's poll() function is called with a 'struct poll_table_struct *wait' argument. This pointer may or may not be NULL, drivers can never rely on it being one or the other as that depends on whether or not an earlier file descriptor in the select()'s fdset matched the requested events. There are only three things a driver can do with the wait argument: 1) obtain the key field: events = wait ? wait->key : ~0; This will still work although it should be replaced with the new poll_requested_events() function (which does exactly the same). This will now even work better, since wait is no longer set to NULL unnecessarily. 2) use the qproc callback. This could be deadly since qproc can now be NULL. Renaming qproc should prevent this from happening. There are no kernel drivers that actually access this callback directly, BTW. 3) test whether wait == NULL to determine whether poll would return without waiting. This is no longer sufficient as the correct test is now wait == NULL || wait->_qproc == NULL. However, the worst that can happen here is a slight performance hit in the case where wait != NULL and wait->_qproc == NULL. In that case the driver will assume that poll_wait() will actually add the fd to the set of waiting file descriptors. Of course, poll_wait() will not do that since it tests for wait->_qproc. This will not break anything, though. There is only one place in the whole kernel where this happens (sock_poll_wait() in include/net/sock.h) and that code will be replaced by a call to poll_does_not_wait() in the next patch. Note that even if wait->_qproc != NULL drivers cannot rely on poll_wait() actually waiting. The next file descriptor from the set might match the event mask and thus any possible waits will never happen. Signed-off-by: Hans Verkuil Reviewed-by: Jonathan Corbet Reviewed-by: Al Viro Cc: Davide Libenzi Signed-off-by: Hans de Goede Cc: Mauro Carvalho Chehab Cc: David Miller Cc: Eric Dumazet Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/eventpoll.c | 18 +++++++++++++++--- fs/select.c | 40 ++++++++++++++++++---------------------- include/linux/poll.h | 37 +++++++++++++++++++++++++++++++------ include/net/sock.h | 2 +- net/unix/af_unix.c | 2 +- 5 files changed, 66 insertions(+), 33 deletions(-) (limited to 'include/linux') diff --git a/fs/eventpoll.c b/fs/eventpoll.c index 4d9d3a45e356..ca300071e79c 100644 --- a/fs/eventpoll.c +++ b/fs/eventpoll.c @@ -699,9 +699,12 @@ static int ep_read_events_proc(struct eventpoll *ep, struct list_head *head, void *priv) { struct epitem *epi, *tmp; + poll_table pt; + init_poll_funcptr(&pt, NULL); list_for_each_entry_safe(epi, tmp, head, rdllink) { - if (epi->ffd.file->f_op->poll(epi->ffd.file, NULL) & + pt._key = epi->event.events; + if (epi->ffd.file->f_op->poll(epi->ffd.file, &pt) & epi->event.events) return POLLIN | POLLRDNORM; else { @@ -1097,6 +1100,7 @@ static int ep_insert(struct eventpoll *ep, struct epoll_event *event, /* Initialize the poll table using the queue callback */ epq.epi = epi; init_poll_funcptr(&epq.pt, ep_ptable_queue_proc); + epq.pt._key = event->events; /* * Attach the item to the poll hooks and get current event bits. @@ -1191,6 +1195,9 @@ static int ep_modify(struct eventpoll *ep, struct epitem *epi, struct epoll_even { int pwake = 0; unsigned int revents; + poll_table pt; + + init_poll_funcptr(&pt, NULL); /* * Set the new event interest mask before calling f_op->poll(); @@ -1198,13 +1205,14 @@ static int ep_modify(struct eventpoll *ep, struct epitem *epi, struct epoll_even * f_op->poll() call and the new event set registering. */ epi->event.events = event->events; + pt._key = event->events; epi->event.data = event->data; /* protected by mtx */ /* * Get current event bits. We can safely use the file* here because * its usage count has been increased by the caller of this function. */ - revents = epi->ffd.file->f_op->poll(epi->ffd.file, NULL); + revents = epi->ffd.file->f_op->poll(epi->ffd.file, &pt); /* * If the item is "hot" and it is not registered inside the ready @@ -1239,6 +1247,9 @@ static int ep_send_events_proc(struct eventpoll *ep, struct list_head *head, unsigned int revents; struct epitem *epi; struct epoll_event __user *uevent; + poll_table pt; + + init_poll_funcptr(&pt, NULL); /* * We can loop without lock because we are passed a task private list. @@ -1251,7 +1262,8 @@ static int ep_send_events_proc(struct eventpoll *ep, struct list_head *head, list_del_init(&epi->rdllink); - revents = epi->ffd.file->f_op->poll(epi->ffd.file, NULL) & + pt._key = epi->event.events; + revents = epi->ffd.file->f_op->poll(epi->ffd.file, &pt) & epi->event.events; /* diff --git a/fs/select.c b/fs/select.c index e782258d0de3..ecfd0b125ba2 100644 --- a/fs/select.c +++ b/fs/select.c @@ -223,7 +223,7 @@ static void __pollwait(struct file *filp, wait_queue_head_t *wait_address, get_file(filp); entry->filp = filp; entry->wait_address = wait_address; - entry->key = p->key; + entry->key = p->_key; init_waitqueue_func_entry(&entry->wait, pollwake); entry->wait.private = pwq; add_wait_queue(wait_address, &entry->wait); @@ -386,13 +386,11 @@ get_max: static inline void wait_key_set(poll_table *wait, unsigned long in, unsigned long out, unsigned long bit) { - if (wait) { - wait->key = POLLEX_SET; - if (in & bit) - wait->key |= POLLIN_SET; - if (out & bit) - wait->key |= POLLOUT_SET; - } + wait->_key = POLLEX_SET; + if (in & bit) + wait->_key |= POLLIN_SET; + if (out & bit) + wait->_key |= POLLOUT_SET; } int do_select(int n, fd_set_bits *fds, struct timespec *end_time) @@ -414,7 +412,7 @@ int do_select(int n, fd_set_bits *fds, struct timespec *end_time) poll_initwait(&table); wait = &table.pt; if (end_time && !end_time->tv_sec && !end_time->tv_nsec) { - wait = NULL; + wait->_qproc = NULL; timed_out = 1; } @@ -459,17 +457,17 @@ int do_select(int n, fd_set_bits *fds, struct timespec *end_time) if ((mask & POLLIN_SET) && (in & bit)) { res_in |= bit; retval++; - wait = NULL; + wait->_qproc = NULL; } if ((mask & POLLOUT_SET) && (out & bit)) { res_out |= bit; retval++; - wait = NULL; + wait->_qproc = NULL; } if ((mask & POLLEX_SET) && (ex & bit)) { res_ex |= bit; retval++; - wait = NULL; + wait->_qproc = NULL; } } } @@ -481,7 +479,7 @@ int do_select(int n, fd_set_bits *fds, struct timespec *end_time) *rexp = res_ex; cond_resched(); } - wait = NULL; + wait->_qproc = NULL; if (retval || timed_out || signal_pending(current)) break; if (table.error) { @@ -720,7 +718,7 @@ struct poll_list { * interested in events matching the pollfd->events mask, and the result * matching that mask is both recorded in pollfd->revents and returned. The * pwait poll_table will be used by the fd-provided poll handler for waiting, - * if non-NULL. + * if pwait->_qproc is non-NULL. */ static inline unsigned int do_pollfd(struct pollfd *pollfd, poll_table *pwait) { @@ -738,9 +736,7 @@ static inline unsigned int do_pollfd(struct pollfd *pollfd, poll_table *pwait) if (file != NULL) { mask = DEFAULT_POLLMASK; if (file->f_op && file->f_op->poll) { - if (pwait) - pwait->key = pollfd->events | - POLLERR | POLLHUP; + pwait->_key = pollfd->events|POLLERR|POLLHUP; mask = file->f_op->poll(file, pwait); } /* Mask out unneeded events. */ @@ -763,7 +759,7 @@ static int do_poll(unsigned int nfds, struct poll_list *list, /* Optimise the no-wait case */ if (end_time && !end_time->tv_sec && !end_time->tv_nsec) { - pt = NULL; + pt->_qproc = NULL; timed_out = 1; } @@ -781,22 +777,22 @@ static int do_poll(unsigned int nfds, struct poll_list *list, for (; pfd != pfd_end; pfd++) { /* * Fish for events. If we found one, record it - * and kill the poll_table, so we don't + * and kill poll_table->_qproc, so we don't * needlessly register any other waiters after * this. They'll get immediately deregistered * when we break out and return. */ if (do_pollfd(pfd, pt)) { count++; - pt = NULL; + pt->_qproc = NULL; } } } /* * All waiters have already been registered, so don't provide - * a poll_table to them on the next loop iteration. + * a poll_table->_qproc to them on the next loop iteration. */ - pt = NULL; + pt->_qproc = NULL; if (!count) { count = wait->error; if (signal_pending(current)) diff --git a/include/linux/poll.h b/include/linux/poll.h index cf40010ce0cd..48fe8bc398d1 100644 --- a/include/linux/poll.h +++ b/include/linux/poll.h @@ -32,21 +32,46 @@ struct poll_table_struct; */ typedef void (*poll_queue_proc)(struct file *, wait_queue_head_t *, struct poll_table_struct *); +/* + * Do not touch the structure directly, use the access functions + * poll_does_not_wait() and poll_requested_events() instead. + */ typedef struct poll_table_struct { - poll_queue_proc qproc; - unsigned long key; + poll_queue_proc _qproc; + unsigned long _key; } poll_table; static inline void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p) { - if (p && wait_address) - p->qproc(filp, wait_address, p); + if (p && p->_qproc && wait_address) + p->_qproc(filp, wait_address, p); +} + +/* + * Return true if it is guaranteed that poll will not wait. This is the case + * if the poll() of another file descriptor in the set got an event, so there + * is no need for waiting. + */ +static inline bool poll_does_not_wait(const poll_table *p) +{ + return p == NULL || p->_qproc == NULL; +} + +/* + * Return the set of events that the application wants to poll for. + * This is useful for drivers that need to know whether a DMA transfer has + * to be started implicitly on poll(). You typically only want to do that + * if the application is actually polling for POLLIN and/or POLLOUT. + */ +static inline unsigned long poll_requested_events(const poll_table *p) +{ + return p ? p->_key : ~0UL; } static inline void init_poll_funcptr(poll_table *pt, poll_queue_proc qproc) { - pt->qproc = qproc; - pt->key = ~0UL; /* all events enabled */ + pt->_qproc = qproc; + pt->_key = ~0UL; /* all events enabled */ } struct poll_table_entry { diff --git a/include/net/sock.h b/include/net/sock.h index 04bc0b30e9e9..a6ba1f8871fd 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -1854,7 +1854,7 @@ static inline bool wq_has_sleeper(struct socket_wq *wq) static inline void sock_poll_wait(struct file *filp, wait_queue_head_t *wait_address, poll_table *p) { - if (p && wait_address) { + if (!poll_does_not_wait(p) && wait_address) { poll_wait(filp, wait_address, p); /* * We need to be sure we are in sync with the diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index eb4277c33188..d510353ef431 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -2206,7 +2206,7 @@ static unsigned int unix_dgram_poll(struct file *file, struct socket *sock, } /* No write status requested, avoid expensive OUT tests. */ - if (wait && !(wait->key & (POLLWRBAND | POLLWRNORM | POLLOUT))) + if (!(poll_requested_events(wait) & (POLLWRBAND|POLLWRNORM|POLLOUT))) return mask; writable = unix_writable(sk); -- cgit v1.2.3 From 15cab952139404d0e593cb1aaab0a3547ac0f95b Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Fri, 23 Mar 2012 15:02:39 -0700 Subject: ptrace: the killed tracee should not enter the syscall Another old/known problem. If the tracee is killed after it reports syscall_entry, it starts the syscall and debugger can't control this. This confuses the users and this creates the security problems for ptrace jailers. Change tracehook_report_syscall_entry() to return non-zero if killed, this instructs syscall_trace_enter() to abort the syscall. Reported-by: Chris Evans Tested-by: Indan Zupancic Signed-off-by: Oleg Nesterov Cc: Denys Vlasenko Cc: Tejun Heo Cc: Pedro Alves Cc: Jan Kratochvil Cc: Steven Rostedt Cc: Frederic Weisbecker Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/tracehook.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/include/linux/tracehook.h b/include/linux/tracehook.h index a71a2927a6a0..51bd91d911c3 100644 --- a/include/linux/tracehook.h +++ b/include/linux/tracehook.h @@ -54,12 +54,12 @@ struct linux_binprm; /* * ptrace report for syscall entry and exit looks identical. */ -static inline void ptrace_report_syscall(struct pt_regs *regs) +static inline int ptrace_report_syscall(struct pt_regs *regs) { int ptrace = current->ptrace; if (!(ptrace & PT_PTRACED)) - return; + return 0; ptrace_notify(SIGTRAP | ((ptrace & PT_TRACESYSGOOD) ? 0x80 : 0)); @@ -72,6 +72,8 @@ static inline void ptrace_report_syscall(struct pt_regs *regs) send_sig(current->exit_code, current, 1); current->exit_code = 0; } + + return fatal_signal_pending(current); } /** @@ -96,8 +98,7 @@ static inline void ptrace_report_syscall(struct pt_regs *regs) static inline __must_check int tracehook_report_syscall_entry( struct pt_regs *regs) { - ptrace_report_syscall(regs); - return 0; + return ptrace_report_syscall(regs); } /** -- cgit v1.2.3 From b1845ff53f1a9eadba005ae53dfe60ab00dfe83b Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Fri, 23 Mar 2012 15:02:40 -0700 Subject: ptrace: don't send SIGTRAP on exec if SEIZED ptrace_event(PTRACE_EVENT_EXEC) sends SIGTRAP if PT_TRACE_EXEC is not set. This is because this SIGTRAP predates PTRACE_O_TRACEEXEC option, we do not need/want this with PT_SEIZED which can set the options during attach. Suggested-by: Pedro Alves Signed-off-by: Oleg Nesterov Cc: Chris Evans Cc: Indan Zupancic Cc: Denys Vlasenko Cc: Tejun Heo Cc: Pedro Alves Cc: Jan Kratochvil Cc: Steven Rostedt Cc: Frederic Weisbecker Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/ptrace.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ptrace.h b/include/linux/ptrace.h index c2f1f6a5fcb8..6fdb196caa3e 100644 --- a/include/linux/ptrace.h +++ b/include/linux/ptrace.h @@ -199,9 +199,10 @@ static inline void ptrace_event(int event, unsigned long message) if (unlikely(ptrace_event_enabled(current, event))) { current->ptrace_message = message; ptrace_notify((event << 8) | SIGTRAP); - } else if (event == PTRACE_EVENT_EXEC && unlikely(current->ptrace)) { + } else if (event == PTRACE_EVENT_EXEC) { /* legacy EXEC report via SIGTRAP */ - send_sig(SIGTRAP, current, 0); + if ((current->ptrace & (PT_PTRACED|PT_SEIZED)) == PT_PTRACED) + send_sig(SIGTRAP, current, 0); } } -- cgit v1.2.3 From 86b6c1f301faf085de5a3f9ce16b8de6e69c729b Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 23 Mar 2012 15:02:41 -0700 Subject: ptrace: simplify PTRACE_foo constants and PTRACE_SETOPTIONS code Exchange PT_TRACESYSGOOD and PT_PTRACE_CAP bit positions, which makes PT_option bits contiguous and therefore makes code in ptrace_setoptions() much simpler. Every PTRACE_O_TRACEevent is defined to (1 << PTRACE_EVENT_event) instead of using explicit numeric constants, to ensure we don't mess up relationship between bit positions and event ids. PT_EVENT_FLAG_SHIFT was not particularly useful, PT_OPT_FLAG_SHIFT with value of PT_EVENT_FLAG_SHIFT-1 is easier to use. PT_TRACE_MASK constant is nuked, the only its use is replaced by (PTRACE_O_MASK << PT_OPT_FLAG_SHIFT). Signed-off-by: Denys Vlasenko Acked-by: Tejun Heo Reviewed-by: Oleg Nesterov Cc: Pedro Alves Cc: Jan Kratochvil Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/ptrace.h | 33 +++++++++++++++------------------ kernel/ptrace.c | 31 ++++++++----------------------- 2 files changed, 23 insertions(+), 41 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ptrace.h b/include/linux/ptrace.h index 6fdb196caa3e..6f1260ee5be5 100644 --- a/include/linux/ptrace.h +++ b/include/linux/ptrace.h @@ -54,17 +54,6 @@ /* flags in @data for PTRACE_SEIZE */ #define PTRACE_SEIZE_DEVEL 0x80000000 /* temp flag for development */ -/* options set using PTRACE_SETOPTIONS */ -#define PTRACE_O_TRACESYSGOOD 0x00000001 -#define PTRACE_O_TRACEFORK 0x00000002 -#define PTRACE_O_TRACEVFORK 0x00000004 -#define PTRACE_O_TRACECLONE 0x00000008 -#define PTRACE_O_TRACEEXEC 0x00000010 -#define PTRACE_O_TRACEVFORKDONE 0x00000020 -#define PTRACE_O_TRACEEXIT 0x00000040 - -#define PTRACE_O_MASK 0x0000007f - /* Wait extended result codes for the above trace options. */ #define PTRACE_EVENT_FORK 1 #define PTRACE_EVENT_VFORK 2 @@ -74,6 +63,17 @@ #define PTRACE_EVENT_EXIT 6 #define PTRACE_EVENT_STOP 7 +/* options set using PTRACE_SETOPTIONS */ +#define PTRACE_O_TRACESYSGOOD 1 +#define PTRACE_O_TRACEFORK (1 << PTRACE_EVENT_FORK) +#define PTRACE_O_TRACEVFORK (1 << PTRACE_EVENT_VFORK) +#define PTRACE_O_TRACECLONE (1 << PTRACE_EVENT_CLONE) +#define PTRACE_O_TRACEEXEC (1 << PTRACE_EVENT_EXEC) +#define PTRACE_O_TRACEVFORKDONE (1 << PTRACE_EVENT_VFORK_DONE) +#define PTRACE_O_TRACEEXIT (1 << PTRACE_EVENT_EXIT) + +#define PTRACE_O_MASK 0x0000007f + #include #ifdef __KERNEL__ @@ -88,13 +88,12 @@ #define PT_SEIZED 0x00010000 /* SEIZE used, enable new behavior */ #define PT_PTRACED 0x00000001 #define PT_DTRACE 0x00000002 /* delayed trace (used on m68k, i386) */ -#define PT_TRACESYSGOOD 0x00000004 -#define PT_PTRACE_CAP 0x00000008 /* ptracer can follow suid-exec */ +#define PT_PTRACE_CAP 0x00000004 /* ptracer can follow suid-exec */ +#define PT_OPT_FLAG_SHIFT 3 /* PT_TRACE_* event enable flags */ -#define PT_EVENT_FLAG_SHIFT 4 -#define PT_EVENT_FLAG(event) (1 << (PT_EVENT_FLAG_SHIFT + (event) - 1)) - +#define PT_EVENT_FLAG(event) (1 << (PT_OPT_FLAG_SHIFT + (event))) +#define PT_TRACESYSGOOD PT_EVENT_FLAG(0) #define PT_TRACE_FORK PT_EVENT_FLAG(PTRACE_EVENT_FORK) #define PT_TRACE_VFORK PT_EVENT_FLAG(PTRACE_EVENT_VFORK) #define PT_TRACE_CLONE PT_EVENT_FLAG(PTRACE_EVENT_CLONE) @@ -102,8 +101,6 @@ #define PT_TRACE_VFORK_DONE PT_EVENT_FLAG(PTRACE_EVENT_VFORK_DONE) #define PT_TRACE_EXIT PT_EVENT_FLAG(PTRACE_EVENT_EXIT) -#define PT_TRACE_MASK 0x000003f4 - /* single stepping state bits (used on ARM and PA-RISC) */ #define PT_SINGLESTEP_BIT 31 #define PT_SINGLESTEP (1<ptrace &= ~PT_TRACE_MASK; - - if (data & PTRACE_O_TRACESYSGOOD) - child->ptrace |= PT_TRACESYSGOOD; - - if (data & PTRACE_O_TRACEFORK) - child->ptrace |= PT_TRACE_FORK; - - if (data & PTRACE_O_TRACEVFORK) - child->ptrace |= PT_TRACE_VFORK; - - if (data & PTRACE_O_TRACECLONE) - child->ptrace |= PT_TRACE_CLONE; - - if (data & PTRACE_O_TRACEEXEC) - child->ptrace |= PT_TRACE_EXEC; - - if (data & PTRACE_O_TRACEVFORKDONE) - child->ptrace |= PT_TRACE_VFORK_DONE; - - if (data & PTRACE_O_TRACEEXIT) - child->ptrace |= PT_TRACE_EXIT; + /* Avoid intermediate state when all opts are cleared */ + flags = child->ptrace; + flags &= ~(PTRACE_O_MASK << PT_OPT_FLAG_SHIFT); + flags |= (data << PT_OPT_FLAG_SHIFT); + child->ptrace = flags; return 0; } -- cgit v1.2.3 From 5cdf389aee90109e2e3d88085dea4dd5508a3be7 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 23 Mar 2012 15:02:42 -0700 Subject: ptrace: renumber PTRACE_EVENT_STOP so that future new options and events can match PTRACE_EVENT_foo and PTRACE_O_TRACEfoo used to match. New PTRACE_EVENT_STOP is the first event which has no corresponding PTRACE_O_TRACE option. If we will ever want to add another such option, its PTRACE_EVENT's value will collide with PTRACE_EVENT_STOP's value. This patch changes PTRACE_EVENT_STOP value to prevent this. While at it, added a comment - the one atop PTRACE_EVENT block, saying "Wait extended result codes for the above trace options", is not true for PTRACE_EVENT_STOP. Signed-off-by: Denys Vlasenko Cc: Tejun Heo Reviewed-by: Oleg Nesterov Cc: Pedro Alves Cc: Jan Kratochvil Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/ptrace.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/ptrace.h b/include/linux/ptrace.h index 6f1260ee5be5..30be18064dfd 100644 --- a/include/linux/ptrace.h +++ b/include/linux/ptrace.h @@ -61,7 +61,8 @@ #define PTRACE_EVENT_EXEC 4 #define PTRACE_EVENT_VFORK_DONE 5 #define PTRACE_EVENT_EXIT 6 -#define PTRACE_EVENT_STOP 7 +/* Extended result codes which enabled by means other than options. */ +#define PTRACE_EVENT_STOP 128 /* options set using PTRACE_SETOPTIONS */ #define PTRACE_O_TRACESYSGOOD 1 -- cgit v1.2.3 From ee00560c7dac1dbbf048446a8489550d0a5765b7 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 23 Mar 2012 15:02:43 -0700 Subject: ptrace: remove PTRACE_SEIZE_DEVEL bit PTRACE_SEIZE code is tested and ready for production use, remove the code which requires special bit in data argument to make PTRACE_SEIZE work. Strace team prepares for a new release of strace, and we would like to ship the code which uses PTRACE_SEIZE, preferably after this change goes into released kernel. Signed-off-by: Denys Vlasenko Acked-by: Tejun Heo Acked-by: Oleg Nesterov Cc: Pedro Alves Cc: Jan Kratochvil Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/ptrace.h | 5 +---- kernel/ptrace.c | 15 --------------- 2 files changed, 1 insertion(+), 19 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ptrace.h b/include/linux/ptrace.h index 30be18064dfd..407c678d2e30 100644 --- a/include/linux/ptrace.h +++ b/include/linux/ptrace.h @@ -51,9 +51,6 @@ #define PTRACE_INTERRUPT 0x4207 #define PTRACE_LISTEN 0x4208 -/* flags in @data for PTRACE_SEIZE */ -#define PTRACE_SEIZE_DEVEL 0x80000000 /* temp flag for development */ - /* Wait extended result codes for the above trace options. */ #define PTRACE_EVENT_FORK 1 #define PTRACE_EVENT_VFORK 2 @@ -64,7 +61,7 @@ /* Extended result codes which enabled by means other than options. */ #define PTRACE_EVENT_STOP 128 -/* options set using PTRACE_SETOPTIONS */ +/* Options set using PTRACE_SETOPTIONS or using PTRACE_SEIZE @data param */ #define PTRACE_O_TRACESYSGOOD 1 #define PTRACE_O_TRACEFORK (1 << PTRACE_EVENT_FORK) #define PTRACE_O_TRACEVFORK (1 << PTRACE_EVENT_VFORK) diff --git a/kernel/ptrace.c b/kernel/ptrace.c index 4661c5bc07e5..ee8d49b9c309 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c @@ -237,25 +237,10 @@ static int ptrace_attach(struct task_struct *task, long request, bool seize = (request == PTRACE_SEIZE); int retval; - /* - * SEIZE will enable new ptrace behaviors which will be implemented - * gradually. SEIZE_DEVEL bit is used to prevent applications - * expecting full SEIZE behaviors trapping on kernel commits which - * are still in the process of implementing them. - * - * Only test programs for new ptrace behaviors being implemented - * should set SEIZE_DEVEL. If unset, SEIZE will fail with -EIO. - * - * Once SEIZE behaviors are completely implemented, this flag - * will be removed. - */ retval = -EIO; if (seize) { if (addr != 0) goto out; - if (!(flags & PTRACE_SEIZE_DEVEL)) - goto out; - flags &= ~(unsigned long)PTRACE_SEIZE_DEVEL; if (flags & ~(unsigned long)PTRACE_O_MASK) goto out; flags = PT_PTRACED | PT_SEIZED | (flags << PT_OPT_FLAG_SHIFT); -- cgit v1.2.3 From d0bd587a80960d7ba7e0c8396e154028c9045c54 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Fri, 23 Mar 2012 15:02:47 -0700 Subject: usermodehelper: implement UMH_KILLABLE Implement UMH_KILLABLE, should be used along with UMH_WAIT_EXEC/PROC. The caller must ensure that subprocess_info->path/etc can not go away until call_usermodehelper_freeinfo(). call_usermodehelper_exec(UMH_KILLABLE) does wait_for_completion_killable. If it fails, it uses xchg(&sub_info->complete, NULL) to serialize with umh_complete() which does the same xhcg() to access sub_info->complete. If call_usermodehelper_exec wins, it can safely return. umh_complete() should get NULL and call call_usermodehelper_freeinfo(). Otherwise we know that umh_complete() was already called, in this case call_usermodehelper_exec() falls back to wait_for_completion() which should succeed "very soon". Note: UMH_NO_WAIT == -1 but it obviously should not be used with UMH_KILLABLE. We delay the neccessary cleanup to simplify the back porting. Signed-off-by: Oleg Nesterov Cc: Tetsuo Handa Cc: Rusty Russell Cc: Tejun Heo Cc: David Rientjes Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/kmod.h | 2 ++ kernel/kmod.c | 27 +++++++++++++++++++++++++-- 2 files changed, 27 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/kmod.h b/include/linux/kmod.h index 722f477c4ef7..1b5985855ffc 100644 --- a/include/linux/kmod.h +++ b/include/linux/kmod.h @@ -54,6 +54,8 @@ enum umh_wait { UMH_WAIT_PROC = 1, /* wait for the process to complete */ }; +#define UMH_KILLABLE 4 /* wait for EXEC/PROC killable */ + struct subprocess_info { struct work_struct work; struct completion *complete; diff --git a/kernel/kmod.c b/kernel/kmod.c index 8ea25944ce33..f92f917c450c 100644 --- a/kernel/kmod.c +++ b/kernel/kmod.c @@ -201,7 +201,15 @@ EXPORT_SYMBOL(call_usermodehelper_freeinfo); static void umh_complete(struct subprocess_info *sub_info) { - complete(sub_info->complete); + struct completion *comp = xchg(&sub_info->complete, NULL); + /* + * See call_usermodehelper_exec(). If xchg() returns NULL + * we own sub_info, the UMH_KILLABLE caller has gone away. + */ + if (comp) + complete(comp); + else + call_usermodehelper_freeinfo(sub_info); } /* Keventd can't block, but this (a child) can. */ @@ -252,6 +260,9 @@ static void __call_usermodehelper(struct work_struct *work) enum umh_wait wait = sub_info->wait; pid_t pid; + if (wait != UMH_NO_WAIT) + wait &= ~UMH_KILLABLE; + /* CLONE_VFORK: wait until the usermode helper has execve'd * successfully We need the data structures to stay around * until that is done. */ @@ -461,9 +472,21 @@ int call_usermodehelper_exec(struct subprocess_info *sub_info, queue_work(khelper_wq, &sub_info->work); if (wait == UMH_NO_WAIT) /* task has freed sub_info */ goto unlock; + + if (wait & UMH_KILLABLE) { + retval = wait_for_completion_killable(&done); + if (!retval) + goto wait_done; + + /* umh_complete() will see NULL and free sub_info */ + if (xchg(&sub_info->complete, NULL)) + goto unlock; + /* fallthrough, umh_complete() was already called */ + } + wait_for_completion(&done); +wait_done: retval = sub_info->retval; - out: call_usermodehelper_freeinfo(sub_info); unlock: -- cgit v1.2.3 From 9d944ef32e83405a07376f112e9f02161d3e9731 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Fri, 23 Mar 2012 15:02:48 -0700 Subject: usermodehelper: kill umh_wait, renumber UMH_* constants No functional changes. It is not sane to use UMH_KILLABLE with enum umh_wait, but obviously we do not want another argument in call_usermodehelper_* helpers. Kill this enum, use the plain int. Signed-off-by: Oleg Nesterov Cc: Tetsuo Handa Cc: Rusty Russell Cc: Tejun Heo Cc: David Rientjes Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/kmod.h | 18 +++++++----------- kernel/kmod.c | 8 ++------ security/keys/request_key.c | 2 +- 3 files changed, 10 insertions(+), 18 deletions(-) (limited to 'include/linux') diff --git a/include/linux/kmod.h b/include/linux/kmod.h index 1b5985855ffc..9efeae679106 100644 --- a/include/linux/kmod.h +++ b/include/linux/kmod.h @@ -48,12 +48,9 @@ static inline int request_module_nowait(const char *name, ...) { return -ENOSYS; struct cred; struct file; -enum umh_wait { - UMH_NO_WAIT = -1, /* don't wait at all */ - UMH_WAIT_EXEC = 0, /* wait for the exec, but not the process */ - UMH_WAIT_PROC = 1, /* wait for the process to complete */ -}; - +#define UMH_NO_WAIT 0 /* don't wait at all */ +#define UMH_WAIT_EXEC 1 /* wait for the exec, but not the process */ +#define UMH_WAIT_PROC 2 /* wait for the process to complete */ #define UMH_KILLABLE 4 /* wait for EXEC/PROC killable */ struct subprocess_info { @@ -62,7 +59,7 @@ struct subprocess_info { char *path; char **argv; char **envp; - enum umh_wait wait; + int wait; int retval; int (*init)(struct subprocess_info *info, struct cred *new); void (*cleanup)(struct subprocess_info *info); @@ -80,15 +77,14 @@ void call_usermodehelper_setfns(struct subprocess_info *info, void *data); /* Actually execute the sub-process */ -int call_usermodehelper_exec(struct subprocess_info *info, enum umh_wait wait); +int call_usermodehelper_exec(struct subprocess_info *info, int wait); /* Free the subprocess_info. This is only needed if you're not going to call call_usermodehelper_exec */ void call_usermodehelper_freeinfo(struct subprocess_info *info); static inline int -call_usermodehelper_fns(char *path, char **argv, char **envp, - enum umh_wait wait, +call_usermodehelper_fns(char *path, char **argv, char **envp, int wait, int (*init)(struct subprocess_info *info, struct cred *new), void (*cleanup)(struct subprocess_info *), void *data) { @@ -106,7 +102,7 @@ call_usermodehelper_fns(char *path, char **argv, char **envp, } static inline int -call_usermodehelper(char *path, char **argv, char **envp, enum umh_wait wait) +call_usermodehelper(char *path, char **argv, char **envp, int wait) { return call_usermodehelper_fns(path, argv, envp, wait, NULL, NULL, NULL); diff --git a/kernel/kmod.c b/kernel/kmod.c index f92f917c450c..8341de91613f 100644 --- a/kernel/kmod.c +++ b/kernel/kmod.c @@ -257,12 +257,9 @@ static void __call_usermodehelper(struct work_struct *work) { struct subprocess_info *sub_info = container_of(work, struct subprocess_info, work); - enum umh_wait wait = sub_info->wait; + int wait = sub_info->wait & ~UMH_KILLABLE; pid_t pid; - if (wait != UMH_NO_WAIT) - wait &= ~UMH_KILLABLE; - /* CLONE_VFORK: wait until the usermode helper has execve'd * successfully We need the data structures to stay around * until that is done. */ @@ -451,8 +448,7 @@ EXPORT_SYMBOL(call_usermodehelper_setfns); * asynchronously if wait is not set, and runs as a child of keventd. * (ie. it runs with full root capabilities). */ -int call_usermodehelper_exec(struct subprocess_info *sub_info, - enum umh_wait wait) +int call_usermodehelper_exec(struct subprocess_info *sub_info, int wait) { DECLARE_COMPLETION_ONSTACK(done); int retval = 0; diff --git a/security/keys/request_key.c b/security/keys/request_key.c index 82465328c39b..cc3790315d2f 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c @@ -91,7 +91,7 @@ static void umh_keys_cleanup(struct subprocess_info *info) * Call a usermode helper with a specific session keyring. */ static int call_usermodehelper_keys(char *path, char **argv, char **envp, - struct key *session_keyring, enum umh_wait wait) + struct key *session_keyring, int wait) { gfp_t gfp_mask = (wait == UMH_NO_WAIT) ? GFP_ATOMIC : GFP_KERNEL; struct subprocess_info *info = -- cgit v1.2.3 From 909af768e88867016f427264ae39d27a57b6a8ed Mon Sep 17 00:00:00 2001 From: Jason Baron Date: Fri, 23 Mar 2012 15:02:51 -0700 Subject: coredump: remove VM_ALWAYSDUMP flag The motivation for this patchset was that I was looking at a way for a qemu-kvm process, to exclude the guest memory from its core dump, which can be quite large. There are already a number of filter flags in /proc//coredump_filter, however, these allow one to specify 'types' of kernel memory, not specific address ranges (which is needed in this case). Since there are no more vma flags available, the first patch eliminates the need for the 'VM_ALWAYSDUMP' flag. The flag is used internally by the kernel to mark vdso and vsyscall pages. However, it is simple enough to check if a vma covers a vdso or vsyscall page without the need for this flag. The second patch then replaces the 'VM_ALWAYSDUMP' flag with a new 'VM_NODUMP' flag, which can be set by userspace using new madvise flags: 'MADV_DONTDUMP', and unset via 'MADV_DODUMP'. The core dump filters continue to work the same as before unless 'MADV_DONTDUMP' is set on the region. The qemu code which implements this features is at: http://people.redhat.com/~jbaron/qemu-dump/qemu-dump.patch In my testing the qemu core dump shrunk from 383MB -> 13MB with this patch. I also believe that the 'MADV_DONTDUMP' flag might be useful for security sensitive apps, which might want to select which areas are dumped. This patch: The VM_ALWAYSDUMP flag is currently used by the coredump code to indicate that a vma is part of a vsyscall or vdso section. However, we can determine if a vma is in one these sections by checking it against the gate_vma and checking for a non-NULL return value from arch_vma_name(). Thus, freeing a valuable vma bit. Signed-off-by: Jason Baron Acked-by: Roland McGrath Cc: Chris Metcalf Cc: Avi Kivity Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/arm/kernel/process.c | 3 +-- arch/hexagon/kernel/vdso.c | 3 +-- arch/mips/kernel/vdso.c | 3 +-- arch/powerpc/kernel/vdso.c | 10 ++-------- arch/s390/kernel/vdso.c | 10 ++-------- arch/sh/kernel/vsyscall/vsyscall.c | 3 +-- arch/tile/mm/elf.c | 8 +------- arch/unicore32/kernel/process.c | 2 +- arch/x86/um/mem_32.c | 8 -------- arch/x86/um/vdso/vma.c | 3 +-- arch/x86/vdso/vdso32-setup.c | 17 ++--------------- arch/x86/vdso/vma.c | 3 +-- fs/binfmt_elf.c | 27 +++++++++++++++++++++++++-- include/linux/mm.h | 1 - mm/memory.c | 8 +------- 15 files changed, 40 insertions(+), 69 deletions(-) (limited to 'include/linux') diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c index c2ae3cd331fe..219e4efee1a6 100644 --- a/arch/arm/kernel/process.c +++ b/arch/arm/kernel/process.c @@ -533,8 +533,7 @@ int vectors_user_mapping(void) struct mm_struct *mm = current->mm; return install_special_mapping(mm, 0xffff0000, PAGE_SIZE, VM_READ | VM_EXEC | - VM_MAYREAD | VM_MAYEXEC | - VM_ALWAYSDUMP | VM_RESERVED, + VM_MAYREAD | VM_MAYEXEC | VM_RESERVED, NULL); } diff --git a/arch/hexagon/kernel/vdso.c b/arch/hexagon/kernel/vdso.c index 16277c33308a..f212a453b527 100644 --- a/arch/hexagon/kernel/vdso.c +++ b/arch/hexagon/kernel/vdso.c @@ -78,8 +78,7 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) /* MAYWRITE to allow gdb to COW and set breakpoints. */ ret = install_special_mapping(mm, vdso_base, PAGE_SIZE, VM_READ|VM_EXEC| - VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC| - VM_ALWAYSDUMP, + VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC, &vdso_page); if (ret) diff --git a/arch/mips/kernel/vdso.c b/arch/mips/kernel/vdso.c index e5cdfd603f8f..0f1af58b036a 100644 --- a/arch/mips/kernel/vdso.c +++ b/arch/mips/kernel/vdso.c @@ -88,8 +88,7 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) ret = install_special_mapping(mm, addr, PAGE_SIZE, VM_READ|VM_EXEC| - VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC| - VM_ALWAYSDUMP, + VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC, &vdso_page); if (ret) diff --git a/arch/powerpc/kernel/vdso.c b/arch/powerpc/kernel/vdso.c index 7d14bb697d40..d36ee1055f88 100644 --- a/arch/powerpc/kernel/vdso.c +++ b/arch/powerpc/kernel/vdso.c @@ -263,17 +263,11 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) * the "data" page of the vDSO or you'll stop getting kernel updates * and your nice userland gettimeofday will be totally dead. * It's fine to use that for setting breakpoints in the vDSO code - * pages though - * - * Make sure the vDSO gets into every core dump. - * Dumping its contents makes post-mortem fully interpretable later - * without matching up the same kernel and hardware config to see - * what PC values meant. + * pages though. */ rc = install_special_mapping(mm, vdso_base, vdso_pages << PAGE_SHIFT, VM_READ|VM_EXEC| - VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC| - VM_ALWAYSDUMP, + VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC, vdso_pagelist); if (rc) { current->mm->context.vdso_base = 0; diff --git a/arch/s390/kernel/vdso.c b/arch/s390/kernel/vdso.c index e704a9965f90..9c80138206b0 100644 --- a/arch/s390/kernel/vdso.c +++ b/arch/s390/kernel/vdso.c @@ -241,17 +241,11 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) * on the "data" page of the vDSO or you'll stop getting kernel * updates and your nice userland gettimeofday will be totally dead. * It's fine to use that for setting breakpoints in the vDSO code - * pages though - * - * Make sure the vDSO gets into every core dump. - * Dumping its contents makes post-mortem fully interpretable later - * without matching up the same kernel and hardware config to see - * what PC values meant. + * pages though. */ rc = install_special_mapping(mm, vdso_base, vdso_pages << PAGE_SHIFT, VM_READ|VM_EXEC| - VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC| - VM_ALWAYSDUMP, + VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC, vdso_pagelist); if (rc) current->mm->context.vdso_base = 0; diff --git a/arch/sh/kernel/vsyscall/vsyscall.c b/arch/sh/kernel/vsyscall/vsyscall.c index 1d6d51a1ce79..5ca579720a09 100644 --- a/arch/sh/kernel/vsyscall/vsyscall.c +++ b/arch/sh/kernel/vsyscall/vsyscall.c @@ -73,8 +73,7 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) ret = install_special_mapping(mm, addr, PAGE_SIZE, VM_READ | VM_EXEC | - VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC | - VM_ALWAYSDUMP, + VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC, syscall_pages); if (unlikely(ret)) goto up_fail; diff --git a/arch/tile/mm/elf.c b/arch/tile/mm/elf.c index 55e58e93bfc5..1a00fb64fc88 100644 --- a/arch/tile/mm/elf.c +++ b/arch/tile/mm/elf.c @@ -117,17 +117,11 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, /* * MAYWRITE to allow gdb to COW and set breakpoints - * - * Make sure the vDSO gets into every core dump. Dumping its - * contents makes post-mortem fully interpretable later - * without matching up the same kernel and hardware config to - * see what PC values meant. */ vdso_base = VDSO_BASE; retval = install_special_mapping(mm, vdso_base, PAGE_SIZE, VM_READ|VM_EXEC| - VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC| - VM_ALWAYSDUMP, + VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC, vdso_pages); #ifndef __tilegx__ diff --git a/arch/unicore32/kernel/process.c b/arch/unicore32/kernel/process.c index 52edc2b62873..432b4291f37b 100644 --- a/arch/unicore32/kernel/process.c +++ b/arch/unicore32/kernel/process.c @@ -381,7 +381,7 @@ int vectors_user_mapping(void) return install_special_mapping(mm, 0xffff0000, PAGE_SIZE, VM_READ | VM_EXEC | VM_MAYREAD | VM_MAYEXEC | - VM_ALWAYSDUMP | VM_RESERVED, + VM_RESERVED, NULL); } diff --git a/arch/x86/um/mem_32.c b/arch/x86/um/mem_32.c index 639900a6fde9..f40281e5d6a2 100644 --- a/arch/x86/um/mem_32.c +++ b/arch/x86/um/mem_32.c @@ -23,14 +23,6 @@ static int __init gate_vma_init(void) gate_vma.vm_flags = VM_READ | VM_MAYREAD | VM_EXEC | VM_MAYEXEC; gate_vma.vm_page_prot = __P101; - /* - * Make sure the vDSO gets into every core dump. - * Dumping its contents makes post-mortem fully interpretable later - * without matching up the same kernel and hardware config to see - * what PC values meant. - */ - gate_vma.vm_flags |= VM_ALWAYSDUMP; - return 0; } __initcall(gate_vma_init); diff --git a/arch/x86/um/vdso/vma.c b/arch/x86/um/vdso/vma.c index 91f4ec9a0a56..af91901babb8 100644 --- a/arch/x86/um/vdso/vma.c +++ b/arch/x86/um/vdso/vma.c @@ -64,8 +64,7 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) err = install_special_mapping(mm, um_vdso_addr, PAGE_SIZE, VM_READ|VM_EXEC| - VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC| - VM_ALWAYSDUMP, + VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC, vdsop); up_write(&mm->mmap_sem); diff --git a/arch/x86/vdso/vdso32-setup.c b/arch/x86/vdso/vdso32-setup.c index 468d591dde31..a944020fa859 100644 --- a/arch/x86/vdso/vdso32-setup.c +++ b/arch/x86/vdso/vdso32-setup.c @@ -250,13 +250,7 @@ static int __init gate_vma_init(void) gate_vma.vm_end = FIXADDR_USER_END; gate_vma.vm_flags = VM_READ | VM_MAYREAD | VM_EXEC | VM_MAYEXEC; gate_vma.vm_page_prot = __P101; - /* - * Make sure the vDSO gets into every core dump. - * Dumping its contents makes post-mortem fully interpretable later - * without matching up the same kernel and hardware config to see - * what PC values meant. - */ - gate_vma.vm_flags |= VM_ALWAYSDUMP; + return 0; } @@ -343,17 +337,10 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) if (compat_uses_vma || !compat) { /* * MAYWRITE to allow gdb to COW and set breakpoints - * - * Make sure the vDSO gets into every core dump. - * Dumping its contents makes post-mortem fully - * interpretable later without matching up the same - * kernel and hardware config to see what PC values - * meant. */ ret = install_special_mapping(mm, addr, PAGE_SIZE, VM_READ|VM_EXEC| - VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC| - VM_ALWAYSDUMP, + VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC, vdso32_pages); if (ret) diff --git a/arch/x86/vdso/vma.c b/arch/x86/vdso/vma.c index 153407c35b75..17e18279649f 100644 --- a/arch/x86/vdso/vma.c +++ b/arch/x86/vdso/vma.c @@ -124,8 +124,7 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) ret = install_special_mapping(mm, addr, vdso_size, VM_READ|VM_EXEC| - VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC| - VM_ALWAYSDUMP, + VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC, vdso_pages); if (ret) { current->mm->context.vdso = NULL; diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index 81878b78c9d4..b64be5b5ac21 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -1092,6 +1092,29 @@ out: * Jeremy Fitzhardinge */ +/* + * The purpose of always_dump_vma() is to make sure that special kernel mappings + * that are useful for post-mortem analysis are included in every core dump. + * In that way we ensure that the core dump is fully interpretable later + * without matching up the same kernel and hardware config to see what PC values + * meant. These special mappings include - vDSO, vsyscall, and other + * architecture specific mappings + */ +static bool always_dump_vma(struct vm_area_struct *vma) +{ + /* Any vsyscall mappings? */ + if (vma == get_gate_vma(vma->vm_mm)) + return true; + /* + * arch_vma_name() returns non-NULL for special architecture mappings, + * such as vDSO sections. + */ + if (arch_vma_name(vma)) + return true; + + return false; +} + /* * Decide what to dump of a segment, part, all or none. */ @@ -1100,8 +1123,8 @@ static unsigned long vma_dump_size(struct vm_area_struct *vma, { #define FILTER(type) (mm_flags & (1UL << MMF_DUMP_##type)) - /* The vma can be set up to tell us the answer directly. */ - if (vma->vm_flags & VM_ALWAYSDUMP) + /* always dump the vdso and vsyscall sections */ + if (always_dump_vma(vma)) goto whole; /* Hugetlb memory check */ diff --git a/include/linux/mm.h b/include/linux/mm.h index 7330742e7973..2de2ddba51d4 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -111,7 +111,6 @@ extern unsigned int kobjsize(const void *objp); #define VM_HUGEPAGE 0x01000000 /* MADV_HUGEPAGE marked this vma */ #endif #define VM_INSERTPAGE 0x02000000 /* The vma has had "vm_insert_page()" done on it */ -#define VM_ALWAYSDUMP 0x04000000 /* Always include in core dumps */ #define VM_CAN_NONLINEAR 0x08000000 /* Has ->fault & does nonlinear pages */ #define VM_MIXEDMAP 0x10000000 /* Can contain "struct page" and pure PFN pages */ diff --git a/mm/memory.c b/mm/memory.c index 3416b6e018d6..6105f475fa86 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -3623,13 +3623,7 @@ static int __init gate_vma_init(void) gate_vma.vm_end = FIXADDR_USER_END; gate_vma.vm_flags = VM_READ | VM_MAYREAD | VM_EXEC | VM_MAYEXEC; gate_vma.vm_page_prot = __P101; - /* - * Make sure the vDSO gets into every core dump. - * Dumping its contents makes post-mortem fully interpretable later - * without matching up the same kernel and hardware config to see - * what PC values meant. - */ - gate_vma.vm_flags |= VM_ALWAYSDUMP; + return 0; } __initcall(gate_vma_init); -- cgit v1.2.3 From accb61fe7bb0f5c2a4102239e4981650f9048519 Mon Sep 17 00:00:00 2001 From: Jason Baron Date: Fri, 23 Mar 2012 15:02:51 -0700 Subject: coredump: add VM_NODUMP, MADV_NODUMP, MADV_CLEAR_NODUMP Since we no longer need the VM_ALWAYSDUMP flag, let's use the freed bit for 'VM_NODUMP' flag. The idea is is to add a new madvise() flag: MADV_DONTDUMP, which can be set by applications to specifically request memory regions which should not dump core. The specific application I have in mind is qemu: we can add a flag there that wouldn't dump all of guest memory when qemu dumps core. This flag might also be useful for security sensitive apps that want to absolutely make sure that parts of memory are not dumped. To clear the flag use: MADV_DODUMP. [akpm@linux-foundation.org: s/MADV_NODUMP/MADV_DONTDUMP/, s/MADV_CLEAR_NODUMP/MADV_DODUMP/, per Roland] [akpm@linux-foundation.org: fix up the architectures which broke] Signed-off-by: Jason Baron Acked-by: Roland McGrath Cc: Chris Metcalf Cc: Avi Kivity Cc: Ralf Baechle Cc: Richard Henderson Cc: Ivan Kokshaysky Cc: Matt Turner Cc: "James E.J. Bottomley" Cc: Helge Deller Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/alpha/include/asm/mman.h | 4 ++++ arch/mips/include/asm/mman.h | 4 ++++ arch/parisc/include/asm/mman.h | 4 ++++ arch/xtensa/include/asm/mman.h | 4 ++++ fs/binfmt_elf.c | 3 +++ include/asm-generic/mman-common.h | 4 ++++ include/linux/mm.h | 1 + mm/madvise.c | 8 ++++++++ 8 files changed, 32 insertions(+) (limited to 'include/linux') diff --git a/arch/alpha/include/asm/mman.h b/arch/alpha/include/asm/mman.h index 72db984f8781..cbeb3616a28e 100644 --- a/arch/alpha/include/asm/mman.h +++ b/arch/alpha/include/asm/mman.h @@ -56,6 +56,10 @@ #define MADV_HUGEPAGE 14 /* Worth backing with hugepages */ #define MADV_NOHUGEPAGE 15 /* Not worth backing with hugepages */ +#define MADV_DONTDUMP 16 /* Explicity exclude from the core dump, + overrides the coredump filter bits */ +#define MADV_DODUMP 17 /* Clear the MADV_NODUMP flag */ + /* compatibility flags */ #define MAP_FILE 0 diff --git a/arch/mips/include/asm/mman.h b/arch/mips/include/asm/mman.h index 785b4ea4ec3f..46d3da0d4b92 100644 --- a/arch/mips/include/asm/mman.h +++ b/arch/mips/include/asm/mman.h @@ -80,6 +80,10 @@ #define MADV_HUGEPAGE 14 /* Worth backing with hugepages */ #define MADV_NOHUGEPAGE 15 /* Not worth backing with hugepages */ +#define MADV_DONTDUMP 16 /* Explicity exclude from the core dump, + overrides the coredump filter bits */ +#define MADV_DODUMP 17 /* Clear the MADV_NODUMP flag */ + /* compatibility flags */ #define MAP_FILE 0 diff --git a/arch/parisc/include/asm/mman.h b/arch/parisc/include/asm/mman.h index f5b7bf5fba68..12219ebce869 100644 --- a/arch/parisc/include/asm/mman.h +++ b/arch/parisc/include/asm/mman.h @@ -62,6 +62,10 @@ #define MADV_HUGEPAGE 67 /* Worth backing with hugepages */ #define MADV_NOHUGEPAGE 68 /* Not worth backing with hugepages */ +#define MADV_DONTDUMP 69 /* Explicity exclude from the core dump, + overrides the coredump filter bits */ +#define MADV_DODUMP 70 /* Clear the MADV_NODUMP flag */ + /* compatibility flags */ #define MAP_FILE 0 #define MAP_VARIABLE 0 diff --git a/arch/xtensa/include/asm/mman.h b/arch/xtensa/include/asm/mman.h index 30789010733d..25bc6c1309c3 100644 --- a/arch/xtensa/include/asm/mman.h +++ b/arch/xtensa/include/asm/mman.h @@ -86,6 +86,10 @@ #define MADV_HUGEPAGE 14 /* Worth backing with hugepages */ #define MADV_NOHUGEPAGE 15 /* Not worth backing with hugepages */ +#define MADV_DONTDUMP 16 /* Explicity exclude from the core dump, + overrides the coredump filter bits */ +#define MADV_DODUMP 17 /* Clear the MADV_NODUMP flag */ + /* compatibility flags */ #define MAP_FILE 0 diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index b64be5b5ac21..504b6eee50a9 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -1127,6 +1127,9 @@ static unsigned long vma_dump_size(struct vm_area_struct *vma, if (always_dump_vma(vma)) goto whole; + if (vma->vm_flags & VM_NODUMP) + return 0; + /* Hugetlb memory check */ if (vma->vm_flags & VM_HUGETLB) { if ((vma->vm_flags & VM_SHARED) && FILTER(HUGETLB_SHARED)) diff --git a/include/asm-generic/mman-common.h b/include/asm-generic/mman-common.h index 787abbb6d867..d030d2c2647a 100644 --- a/include/asm-generic/mman-common.h +++ b/include/asm-generic/mman-common.h @@ -48,6 +48,10 @@ #define MADV_HUGEPAGE 14 /* Worth backing with hugepages */ #define MADV_NOHUGEPAGE 15 /* Not worth backing with hugepages */ +#define MADV_DONTDUMP 16 /* Explicity exclude from the core dump, + overrides the coredump filter bits */ +#define MADV_DODUMP 17 /* Clear the MADV_NODUMP flag */ + /* compatibility flags */ #define MAP_FILE 0 diff --git a/include/linux/mm.h b/include/linux/mm.h index 2de2ddba51d4..a6fabdfd34c5 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -111,6 +111,7 @@ extern unsigned int kobjsize(const void *objp); #define VM_HUGEPAGE 0x01000000 /* MADV_HUGEPAGE marked this vma */ #endif #define VM_INSERTPAGE 0x02000000 /* The vma has had "vm_insert_page()" done on it */ +#define VM_NODUMP 0x04000000 /* Do not include in the core dump */ #define VM_CAN_NONLINEAR 0x08000000 /* Has ->fault & does nonlinear pages */ #define VM_MIXEDMAP 0x10000000 /* Can contain "struct page" and pure PFN pages */ diff --git a/mm/madvise.c b/mm/madvise.c index f5ab745672b7..1ccbba5b6674 100644 --- a/mm/madvise.c +++ b/mm/madvise.c @@ -65,6 +65,12 @@ static long madvise_behavior(struct vm_area_struct * vma, } new_flags &= ~VM_DONTCOPY; break; + case MADV_DONTDUMP: + new_flags |= VM_NODUMP; + break; + case MADV_DODUMP: + new_flags &= ~VM_NODUMP; + break; case MADV_MERGEABLE: case MADV_UNMERGEABLE: error = ksm_madvise(vma, start, end, behavior, &new_flags); @@ -293,6 +299,8 @@ madvise_behavior_valid(int behavior) case MADV_HUGEPAGE: case MADV_NOHUGEPAGE: #endif + case MADV_DONTDUMP: + case MADV_DODUMP: return 1; default: -- cgit v1.2.3 From 1ac101a5d675aca2426c5cd460c73fb95acb8391 Mon Sep 17 00:00:00 2001 From: KAMEZAWA Hiroyuki Date: Fri, 23 Mar 2012 15:02:54 -0700 Subject: procfs: add num_to_str() to speed up /proc/stat == stat_check.py num = 0 with open("/proc/stat") as f: while num < 1000 : data = f.read() f.seek(0, 0) num = num + 1 == perf shows 20.39% stat_check.py [kernel.kallsyms] [k] format_decode 13.41% stat_check.py [kernel.kallsyms] [k] number 12.61% stat_check.py [kernel.kallsyms] [k] vsnprintf 10.85% stat_check.py [kernel.kallsyms] [k] memcpy 4.85% stat_check.py [kernel.kallsyms] [k] radix_tree_lookup 4.43% stat_check.py [kernel.kallsyms] [k] seq_printf This patch removes most of calls to vsnprintf() by adding num_to_str() and seq_print_decimal_ull(), which prints decimal numbers without rich functions provided by printf(). On my 8cpu box. == Before patch == [root@bluextal test]# time ./stat_check.py real 0m0.150s user 0m0.026s sys 0m0.121s == After patch == [root@bluextal test]# time ./stat_check.py real 0m0.055s user 0m0.022s sys 0m0.030s [akpm@linux-foundation.org: remove incorrect comment, use less statck in num_to_str(), move comment from .h to .c, simplify seq_put_decimal_ull()] [andrea@betterlinux.com: avoid breaking the ABI in /proc/stat] Signed-off-by: KAMEZAWA Hiroyuki Signed-off-by: Andrea Righi Cc: Eric Dumazet Cc: Glauber Costa Cc: Peter Zijlstra Cc: Ingo Molnar Cc: Paul Turner Cc: Russell King Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/proc/stat.c | 55 ++++++++++++++++++++++++------------------------ fs/seq_file.c | 33 +++++++++++++++++++++++++++++ include/linux/kernel.h | 2 ++ include/linux/seq_file.h | 3 ++- lib/vsprintf.c | 20 ++++++++++++++++++ 5 files changed, 84 insertions(+), 29 deletions(-) (limited to 'include/linux') diff --git a/fs/proc/stat.c b/fs/proc/stat.c index ac446114cd48..6a0c62d6e442 100644 --- a/fs/proc/stat.c +++ b/fs/proc/stat.c @@ -89,18 +89,19 @@ static int show_stat(struct seq_file *p, void *v) } sum += arch_irq_stat(); - seq_printf(p, "cpu %llu %llu %llu %llu %llu %llu %llu %llu %llu " - "%llu\n", - (unsigned long long)cputime64_to_clock_t(user), - (unsigned long long)cputime64_to_clock_t(nice), - (unsigned long long)cputime64_to_clock_t(system), - (unsigned long long)cputime64_to_clock_t(idle), - (unsigned long long)cputime64_to_clock_t(iowait), - (unsigned long long)cputime64_to_clock_t(irq), - (unsigned long long)cputime64_to_clock_t(softirq), - (unsigned long long)cputime64_to_clock_t(steal), - (unsigned long long)cputime64_to_clock_t(guest), - (unsigned long long)cputime64_to_clock_t(guest_nice)); + seq_puts(p, "cpu "); + seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(user)); + seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(nice)); + seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(system)); + seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(idle)); + seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(iowait)); + seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(irq)); + seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(softirq)); + seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(steal)); + seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(guest)); + seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(guest_nice)); + seq_putc(p, '\n'); + for_each_online_cpu(i) { /* Copy values here to work around gcc-2.95.3, gcc-2.96 */ user = kcpustat_cpu(i).cpustat[CPUTIME_USER]; @@ -113,26 +114,24 @@ static int show_stat(struct seq_file *p, void *v) steal = kcpustat_cpu(i).cpustat[CPUTIME_STEAL]; guest = kcpustat_cpu(i).cpustat[CPUTIME_GUEST]; guest_nice = kcpustat_cpu(i).cpustat[CPUTIME_GUEST_NICE]; - seq_printf(p, - "cpu%d %llu %llu %llu %llu %llu %llu %llu %llu %llu " - "%llu\n", - i, - (unsigned long long)cputime64_to_clock_t(user), - (unsigned long long)cputime64_to_clock_t(nice), - (unsigned long long)cputime64_to_clock_t(system), - (unsigned long long)cputime64_to_clock_t(idle), - (unsigned long long)cputime64_to_clock_t(iowait), - (unsigned long long)cputime64_to_clock_t(irq), - (unsigned long long)cputime64_to_clock_t(softirq), - (unsigned long long)cputime64_to_clock_t(steal), - (unsigned long long)cputime64_to_clock_t(guest), - (unsigned long long)cputime64_to_clock_t(guest_nice)); + seq_printf(p, "cpu%d", i); + seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(user)); + seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(nice)); + seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(system)); + seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(idle)); + seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(iowait)); + seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(irq)); + seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(softirq)); + seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(steal)); + seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(guest)); + seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(guest_nice)); + seq_putc(p, '\n'); } seq_printf(p, "intr %llu", (unsigned long long)sum); /* sum again ? it could be updated? */ for_each_irq_nr(j) - seq_printf(p, " %u", kstat_irqs(j)); + seq_put_decimal_ull(p, ' ', kstat_irqs(j)); seq_printf(p, "\nctxt %llu\n" @@ -149,7 +148,7 @@ static int show_stat(struct seq_file *p, void *v) seq_printf(p, "softirq %llu", (unsigned long long)sum_softirq); for (i = 0; i < NR_SOFTIRQS; i++) - seq_printf(p, " %u", per_softirq_sums[i]); + seq_put_decimal_ull(p, ' ', per_softirq_sums[i]); seq_putc(p, '\n'); return 0; diff --git a/fs/seq_file.c b/fs/seq_file.c index aa242dc99373..7d19816c4cc9 100644 --- a/fs/seq_file.c +++ b/fs/seq_file.c @@ -644,6 +644,39 @@ int seq_puts(struct seq_file *m, const char *s) } EXPORT_SYMBOL(seq_puts); +/* + * A helper routine for putting decimal numbers without rich format of printf(). + * only 'unsigned long long' is supported. + * This routine will put one byte delimiter + number into seq_file. + * This routine is very quick when you show lots of numbers. + * In usual cases, it will be better to use seq_printf(). It's easier to read. + */ +int seq_put_decimal_ull(struct seq_file *m, char delimiter, + unsigned long long num) +{ + int len; + + if (m->count + 2 >= m->size) /* we'll write 2 bytes at least */ + goto overflow; + + m->buf[m->count++] = delimiter; + + if (num < 10) { + m->buf[m->count++] = num + '0'; + return 0; + } + + len = num_to_str(m->buf + m->count, m->size - m->count, num); + if (!len) + goto overflow; + m->count += len; + return 0; +overflow: + m->count = m->size; + return -1; +} +EXPORT_SYMBOL(seq_put_decimal_ull); + /** * seq_write - write arbitrary data to buffer * @seq: seq_file identifying the buffer to which data should be written diff --git a/include/linux/kernel.h b/include/linux/kernel.h index f2085b541a24..3e140add5360 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -312,6 +312,8 @@ extern long long simple_strtoll(const char *,char **,unsigned int); #define strict_strtoull kstrtoull #define strict_strtoll kstrtoll +extern int num_to_str(char *buf, int size, unsigned long long num); + /* lib/printf utilities */ extern __printf(2, 3) int sprintf(char *buf, const char * fmt, ...); diff --git a/include/linux/seq_file.h b/include/linux/seq_file.h index 44f1514b00ba..5bba42c99448 100644 --- a/include/linux/seq_file.h +++ b/include/linux/seq_file.h @@ -121,9 +121,10 @@ int single_release(struct inode *, struct file *); void *__seq_open_private(struct file *, const struct seq_operations *, int); int seq_open_private(struct file *, const struct seq_operations *, int); int seq_release_private(struct inode *, struct file *); +int seq_put_decimal_ull(struct seq_file *m, char delimiter, + unsigned long long num); #define SEQ_START_TOKEN ((void *)1) - /* * Helpers for iteration over list_head-s in seq_files */ diff --git a/lib/vsprintf.c b/lib/vsprintf.c index 38e612e66da5..385c40291cdb 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -212,6 +212,26 @@ char *put_dec(char *buf, unsigned long long num) } } +/* + * Convert passed number to decimal string. + * Returns the length of string. On buffer overflow, returns 0. + * + * If speed is not important, use snprintf(). It's easy to read the code. + */ +int num_to_str(char *buf, int size, unsigned long long num) +{ + char tmp[21]; /* Enough for 2^64 in decimal */ + int idx, len; + + len = put_dec(tmp, num) - tmp; + + if (len > size) + return 0; + for (idx = 0; idx < len; ++idx) + buf[idx] = tmp[len - idx - 1]; + return len; +} + #define ZEROPAD 1 /* pad with zero */ #define SIGN 2 /* unsigned/signed long */ #define PLUS 4 /* show plus */ -- cgit v1.2.3 From bda7bad62bc4c4e0783348e8db51abe094153c56 Mon Sep 17 00:00:00 2001 From: KAMEZAWA Hiroyuki Date: Fri, 23 Mar 2012 15:02:54 -0700 Subject: procfs: speed up /proc/pid/stat, statm Process accounting applications as top, ps visit some files under /proc/. With seq_put_decimal_ull(), we can optimize /proc//stat and /proc//statm files. This patch adds - seq_put_decimal_ll() for signed values. - allow delimiter == 0. - convert seq_printf() to seq_put_decimal_ull/ll in /proc/stat, statm. Test result on a system with 2000+ procs. Before patch: [kamezawa@bluextal test]$ top -b -n 1 | wc -l 2223 [kamezawa@bluextal test]$ time top -b -n 1 > /dev/null real 0m0.675s user 0m0.044s sys 0m0.121s [kamezawa@bluextal test]$ time ps -elf > /dev/null real 0m0.236s user 0m0.056s sys 0m0.176s After patch: kamezawa@bluextal ~]$ time top -b -n 1 > /dev/null real 0m0.657s user 0m0.052s sys 0m0.100s [kamezawa@bluextal ~]$ time ps -elf > /dev/null real 0m0.198s user 0m0.050s sys 0m0.145s Considering top, ps tend to scan /proc periodically, this will reduce cpu consumption by top/ps to some extent. [akpm@linux-foundation.org: checkpatch fixes] Signed-off-by: KAMEZAWA Hiroyuki Cc: Alexey Dobriyan Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/proc/array.c | 119 +++++++++++++++++++++++++---------------------- fs/seq_file.c | 21 ++++++++- include/linux/seq_file.h | 2 + 3 files changed, 86 insertions(+), 56 deletions(-) (limited to 'include/linux') diff --git a/fs/proc/array.c b/fs/proc/array.c index c602b8d20f06..fbb53c249086 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -462,59 +462,56 @@ static int do_task_stat(struct seq_file *m, struct pid_namespace *ns, /* convert nsec -> ticks */ start_time = nsec_to_clock_t(start_time); - seq_printf(m, "%d (%s) %c %d %d %d %d %d %u %lu \ -%lu %lu %lu %lu %lu %ld %ld %ld %ld %d 0 %llu %lu %ld %lu %lu %lu %lu %lu \ -%lu %lu %lu %lu %lu %lu %lu %lu %d %d %u %u %llu %lu %ld %lu %lu %lu\n", - pid_nr_ns(pid, ns), - tcomm, - state, - ppid, - pgid, - sid, - tty_nr, - tty_pgrp, - task->flags, - min_flt, - cmin_flt, - maj_flt, - cmaj_flt, - cputime_to_clock_t(utime), - cputime_to_clock_t(stime), - cputime_to_clock_t(cutime), - cputime_to_clock_t(cstime), - priority, - nice, - num_threads, - start_time, - vsize, - mm ? get_mm_rss(mm) : 0, - rsslim, - mm ? (permitted ? mm->start_code : 1) : 0, - mm ? (permitted ? mm->end_code : 1) : 0, - (permitted && mm) ? mm->start_stack : 0, - esp, - eip, - /* The signal information here is obsolete. - * It must be decimal for Linux 2.0 compatibility. - * Use /proc/#/status for real-time signals. - */ - task->pending.signal.sig[0] & 0x7fffffffUL, - task->blocked.sig[0] & 0x7fffffffUL, - sigign .sig[0] & 0x7fffffffUL, - sigcatch .sig[0] & 0x7fffffffUL, - wchan, - 0UL, - 0UL, - task->exit_signal, - task_cpu(task), - task->rt_priority, - task->policy, - (unsigned long long)delayacct_blkio_ticks(task), - cputime_to_clock_t(gtime), - cputime_to_clock_t(cgtime), - (mm && permitted) ? mm->start_data : 0, - (mm && permitted) ? mm->end_data : 0, - (mm && permitted) ? mm->start_brk : 0); + seq_printf(m, "%d (%s) %c", pid_nr_ns(pid, ns), tcomm, state); + seq_put_decimal_ll(m, ' ', ppid); + seq_put_decimal_ll(m, ' ', pgid); + seq_put_decimal_ll(m, ' ', sid); + seq_put_decimal_ll(m, ' ', tty_nr); + seq_put_decimal_ll(m, ' ', tty_pgrp); + seq_put_decimal_ull(m, ' ', task->flags); + seq_put_decimal_ull(m, ' ', min_flt); + seq_put_decimal_ull(m, ' ', cmin_flt); + seq_put_decimal_ull(m, ' ', maj_flt); + seq_put_decimal_ull(m, ' ', cmaj_flt); + seq_put_decimal_ull(m, ' ', cputime_to_clock_t(utime)); + seq_put_decimal_ull(m, ' ', cputime_to_clock_t(stime)); + seq_put_decimal_ll(m, ' ', cputime_to_clock_t(cutime)); + seq_put_decimal_ll(m, ' ', cputime_to_clock_t(cstime)); + seq_put_decimal_ll(m, ' ', priority); + seq_put_decimal_ll(m, ' ', nice); + seq_put_decimal_ll(m, ' ', num_threads); + seq_put_decimal_ull(m, ' ', 0); + seq_put_decimal_ull(m, ' ', start_time); + seq_put_decimal_ull(m, ' ', vsize); + seq_put_decimal_ll(m, ' ', mm ? get_mm_rss(mm) : 0); + seq_put_decimal_ull(m, ' ', rsslim); + seq_put_decimal_ull(m, ' ', mm ? (permitted ? mm->start_code : 1) : 0); + seq_put_decimal_ull(m, ' ', mm ? (permitted ? mm->end_code : 1) : 0); + seq_put_decimal_ull(m, ' ', (permitted && mm) ? mm->start_stack : 0); + seq_put_decimal_ull(m, ' ', esp); + seq_put_decimal_ull(m, ' ', eip); + /* The signal information here is obsolete. + * It must be decimal for Linux 2.0 compatibility. + * Use /proc/#/status for real-time signals. + */ + seq_put_decimal_ull(m, ' ', task->pending.signal.sig[0] & 0x7fffffffUL); + seq_put_decimal_ull(m, ' ', task->blocked.sig[0] & 0x7fffffffUL); + seq_put_decimal_ull(m, ' ', sigign.sig[0] & 0x7fffffffUL); + seq_put_decimal_ull(m, ' ', sigcatch.sig[0] & 0x7fffffffUL); + seq_put_decimal_ull(m, ' ', wchan); + seq_put_decimal_ull(m, ' ', 0); + seq_put_decimal_ull(m, ' ', 0); + seq_put_decimal_ll(m, ' ', task->exit_signal); + seq_put_decimal_ll(m, ' ', task_cpu(task)); + seq_put_decimal_ull(m, ' ', task->rt_priority); + seq_put_decimal_ull(m, ' ', task->policy); + seq_put_decimal_ull(m, ' ', delayacct_blkio_ticks(task)); + seq_put_decimal_ull(m, ' ', cputime_to_clock_t(gtime)); + seq_put_decimal_ll(m, ' ', cputime_to_clock_t(cgtime)); + seq_put_decimal_ull(m, ' ', (mm && permitted) ? mm->start_data : 0); + seq_put_decimal_ull(m, ' ', (mm && permitted) ? mm->end_data : 0); + seq_put_decimal_ull(m, ' ', (mm && permitted) ? mm->start_brk : 0); + seq_putc(m, '\n'); if (mm) mmput(mm); return 0; @@ -542,8 +539,20 @@ int proc_pid_statm(struct seq_file *m, struct pid_namespace *ns, size = task_statm(mm, &shared, &text, &data, &resident); mmput(mm); } - seq_printf(m, "%lu %lu %lu %lu 0 %lu 0\n", - size, resident, shared, text, data); + /* + * For quick read, open code by putting numbers directly + * expected format is + * seq_printf(m, "%lu %lu %lu %lu 0 %lu 0\n", + * size, resident, shared, text, data); + */ + seq_put_decimal_ull(m, 0, size); + seq_put_decimal_ull(m, ' ', resident); + seq_put_decimal_ull(m, ' ', shared); + seq_put_decimal_ull(m, ' ', text); + seq_put_decimal_ull(m, ' ', 0); + seq_put_decimal_ull(m, ' ', text); + seq_put_decimal_ull(m, ' ', 0); + seq_putc(m, '\n'); return 0; } diff --git a/fs/seq_file.c b/fs/seq_file.c index 7d19816c4cc9..55c293f7024d 100644 --- a/fs/seq_file.c +++ b/fs/seq_file.c @@ -659,7 +659,8 @@ int seq_put_decimal_ull(struct seq_file *m, char delimiter, if (m->count + 2 >= m->size) /* we'll write 2 bytes at least */ goto overflow; - m->buf[m->count++] = delimiter; + if (delimiter) + m->buf[m->count++] = delimiter; if (num < 10) { m->buf[m->count++] = num + '0'; @@ -677,6 +678,24 @@ overflow: } EXPORT_SYMBOL(seq_put_decimal_ull); +int seq_put_decimal_ll(struct seq_file *m, char delimiter, + long long num) +{ + if (num < 0) { + if (m->count + 3 >= m->size) { + m->count = m->size; + return -1; + } + if (delimiter) + m->buf[m->count++] = delimiter; + num = -num; + delimiter = '-'; + } + return seq_put_decimal_ull(m, delimiter, num); + +} +EXPORT_SYMBOL(seq_put_decimal_ll); + /** * seq_write - write arbitrary data to buffer * @seq: seq_file identifying the buffer to which data should be written diff --git a/include/linux/seq_file.h b/include/linux/seq_file.h index 5bba42c99448..54e5ae7f8adc 100644 --- a/include/linux/seq_file.h +++ b/include/linux/seq_file.h @@ -123,6 +123,8 @@ int seq_open_private(struct file *, const struct seq_operations *, int); int seq_release_private(struct inode *, struct file *); int seq_put_decimal_ull(struct seq_file *m, char delimiter, unsigned long long num); +int seq_put_decimal_ll(struct seq_file *m, char delimiter, + long long num); #define SEQ_START_TOKEN ((void *)1) /* -- cgit v1.2.3 From 50269e19ad990e79eeda101fc6df80cffd5d4831 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 23 Mar 2012 23:59:33 +0000 Subject: net: add a truesize parameter to skb_add_rx_frag() skb_add_rx_frag() API is misleading. Network skbs built with this helper can use uncharged kernel memory and eventually stress/crash machine in OOM. Add a 'truesize' parameter and then fix drivers in followup patches. Signed-off-by: Eric Dumazet Cc: Wey-Yi Guy Signed-off-by: David S. Miller --- drivers/net/usb/cdc-phonet.c | 6 ++++-- drivers/net/wireless/iwlegacy/3945.c | 3 ++- drivers/net/wireless/iwlegacy/4965-mac.c | 3 ++- drivers/net/wireless/iwlwifi/iwl-agn-rx.c | 2 +- drivers/usb/gadget/f_phonet.c | 2 +- include/linux/skbuff.h | 2 +- net/core/skbuff.c | 4 ++-- 7 files changed, 13 insertions(+), 9 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/usb/cdc-phonet.c b/drivers/net/usb/cdc-phonet.c index 790cbdea7392..3886b30ed373 100644 --- a/drivers/net/usb/cdc-phonet.c +++ b/drivers/net/usb/cdc-phonet.c @@ -164,12 +164,14 @@ static void rx_complete(struct urb *req) /* Can't use pskb_pull() on page in IRQ */ memcpy(skb_put(skb, 1), page_address(page), 1); skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, - page, 1, req->actual_length); + page, 1, req->actual_length, + req->actual_length); page = NULL; } } else { skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, - page, 0, req->actual_length); + page, 0, req->actual_length, + req->actual_length); page = NULL; } if (req->actual_length < PAGE_SIZE) diff --git a/drivers/net/wireless/iwlegacy/3945.c b/drivers/net/wireless/iwlegacy/3945.c index c5b1d199e0bc..b25c01be0d90 100644 --- a/drivers/net/wireless/iwlegacy/3945.c +++ b/drivers/net/wireless/iwlegacy/3945.c @@ -499,7 +499,8 @@ il3945_pass_packet_to_mac80211(struct il_priv *il, struct il_rx_buf *rxb, le32_to_cpu(rx_end->status), stats); skb_add_rx_frag(skb, 0, rxb->page, - (void *)rx_hdr->payload - (void *)pkt, len); + (void *)rx_hdr->payload - (void *)pkt, len, + len); il_update_stats(il, false, fc, len); memcpy(IEEE80211_SKB_RXCB(skb), stats, sizeof(*stats)); diff --git a/drivers/net/wireless/iwlegacy/4965-mac.c b/drivers/net/wireless/iwlegacy/4965-mac.c index 7b54dbb338be..17f1c6853182 100644 --- a/drivers/net/wireless/iwlegacy/4965-mac.c +++ b/drivers/net/wireless/iwlegacy/4965-mac.c @@ -596,7 +596,8 @@ il4965_pass_packet_to_mac80211(struct il_priv *il, struct ieee80211_hdr *hdr, return; } - skb_add_rx_frag(skb, 0, rxb->page, (void *)hdr - rxb_addr(rxb), len); + skb_add_rx_frag(skb, 0, rxb->page, (void *)hdr - rxb_addr(rxb), len, + len); il_update_stats(il, false, fc, len); memcpy(IEEE80211_SKB_RXCB(skb), stats, sizeof(*stats)); diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rx.c b/drivers/net/wireless/iwlwifi/iwl-agn-rx.c index 44c6f712b77d..f4b84d1596e3 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-rx.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-rx.c @@ -796,7 +796,7 @@ static void iwlagn_pass_packet_to_mac80211(struct iwl_priv *priv, offset = (void *)hdr - rxb_addr(rxb); p = rxb_steal_page(rxb); - skb_add_rx_frag(skb, 0, p, offset, len); + skb_add_rx_frag(skb, 0, p, offset, len, len); iwl_update_stats(priv, false, fc, len); diff --git a/drivers/usb/gadget/f_phonet.c b/drivers/usb/gadget/f_phonet.c index 7cdcb63b21ff..85a5cebe96b3 100644 --- a/drivers/usb/gadget/f_phonet.c +++ b/drivers/usb/gadget/f_phonet.c @@ -345,7 +345,7 @@ static void pn_rx_complete(struct usb_ep *ep, struct usb_request *req) } skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, page, - skb->len <= 1, req->actual); + skb->len <= 1, req->actual, req->actual); page = NULL; if (req->actual < req->length) { /* Last fragment */ diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index a2b9953b582d..681a18799140 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -1244,7 +1244,7 @@ static inline void skb_fill_page_desc(struct sk_buff *skb, int i, } extern void skb_add_rx_frag(struct sk_buff *skb, int i, struct page *page, - int off, int size); + int off, int size, unsigned int truesize); #define SKB_PAGE_ASSERT(skb) BUG_ON(skb_shinfo(skb)->nr_frags) #define SKB_FRAG_ASSERT(skb) BUG_ON(skb_has_frag_list(skb)) diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 6eb656acdfe5..a690cae91cdd 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -321,12 +321,12 @@ struct sk_buff *__netdev_alloc_skb(struct net_device *dev, EXPORT_SYMBOL(__netdev_alloc_skb); void skb_add_rx_frag(struct sk_buff *skb, int i, struct page *page, int off, - int size) + int size, unsigned int truesize) { skb_fill_page_desc(skb, i, page, off, size); skb->len += size; skb->data_len += size; - skb->truesize += size; + skb->truesize += truesize; } EXPORT_SYMBOL(skb_add_rx_frag); -- cgit v1.2.3