Kernel code may export it for complete or partial access.
GPIOs are identified as they are inside the kernel, using integers in
- the range 0..INT_MAX. See Documentation/gpio/gpio.txt for more information.
+ the range 0..INT_MAX. See Documentation/gpio for more information.
/sys/class/gpio
/export ... asks the kernel to export a GPIO to userspace
See files in Documentation/cpu-freq/ for more information.
- In particular, read Documentation/cpu-freq/user-guide.txt
- to learn how to control the knobs.
-
What: /sys/devices/system/cpu/cpu#/cpufreq/freqdomain_cpus
Date: June 2013
Control touchpad mode.
* 1 -> Switched On
* 0 -> Switched Off
+
+What: /sys/bus/pci/devices/<bdf>/<device>/VPC2004:00/fn_lock
+Date: May 2018
+KernelVersion: 4.18
+Contact: "Oleg Keri <ezhi99@gmail.com>"
+Description:
+ Control fn-lock mode.
+ * 1 -> Switched On
+ * 0 -> Switched Off
+
+ For example:
+ # echo "0" > \
+ /sys/bus/pci/devices/0000:00:1f.0/PNP0C09:00/VPC2004:00/fn_lock
Mailing List - apparmor@lists.ubuntu.com
-Wiki - http://apparmor.wiki.kernel.org/
+Wiki - http://wiki.apparmor.net
-User space tools - https://launchpad.net/apparmor
+User space tools - https://gitlab.com/apparmor
-Kernel module - git://git.kernel.org/pub/scm/linux/kernel/git/jj/apparmor-dev.git
+Kernel module - git://git.kernel.org/pub/scm/linux/kernel/git/jj/linux-apparmor
(may crash computer or cause data corruption)
ALSA [HW,ALSA]
- See Documentation/sound/alsa/alsa-parameters.txt
+ See Documentation/sound/alsa-configuration.rst
alignment= [KNL,ARM]
Allow the default userspace alignment fault handler
This will also cause panics on machine check exceptions.
Useful together with panic=30 to trigger a reboot.
- OSS [HW,OSS]
- See Documentation/sound/oss/oss-parameters.txt
-
page_owner= [KNL] Boot-time page_owner enabling option.
Storage of the information about who allocated
each page is disabled in default. With this switch,
[FTRACE] Set and start specified trace events in order
to facilitate early boot debugging. The event-list is a
comma separated list of trace events to enable. See
- also Documentation/trace/events.txt
+ also Documentation/trace/events.rst
trace_options=[option-list]
[FTRACE] Enable or disable tracer options at boot.
trace_options=stacktrace
- See also Documentation/trace/ftrace.txt "trace options"
+ See also Documentation/trace/ftrace.rst "trace options"
section.
tp_printk[FTRACE]
operations before calling end_that_request_last()! For an example of a user
of these helpers, see the IDE tagged command queueing support.
-Certain hardware conditions may dictate a need to invalidate the block tag
-queue. For instance, on IDE any tagged request error needs to clear both
-the hardware and software block queue and enable the driver to sanely restart
-all the outstanding requests. There's a third helper to do that:
-
- blk_queue_invalidate_tags(struct request_queue *q)
-
- Clear the internal block tag queue and re-add all the pending requests
- to the request queue. The driver will receive them again on the
- next request_fn run, just like it did the first time it encountered
- them.
-
3.2.5.2 Tag info
Some block functions exist to query current tag status or to go from a
Most of the above is simple and straight forward, however busy_list may need
a bit of explaining. Normally we don't care too much about request ordering,
but in the event of any barrier requests in the tag queue we need to ensure
-that requests are restarted in the order they were queue. This may happen
-if the driver needs to use blk_queue_invalidate_tags().
+that requests are restarted in the order they were queue.
3.3 I/O Submission
MTRR Handling
-------------
-.. kernel-doc:: arch/x86/kernel/cpu/mtrr/main.c
+.. kernel-doc:: arch/x86/kernel/cpu/mtrr/mtrr.c
:export:
Security Framework
Requirement
-----------
-You have to put at start of your tfm_ctx the struct crypto_engine_ctx
-struct your_tfm_ctx {
+You have to put at start of your tfm_ctx the struct crypto_engine_ctx::
+
+ struct your_tfm_ctx {
struct crypto_engine_ctx enginectx;
...
-};
+ };
+
Why: Since CE manage only crypto_async_request, it cannot know the underlying
request_type and so have access only on the TFM.
So using container_of for accessing __ctx is impossible.
Each subnode should use the binding described in [2]..[7]
[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
-[3] Documentation/devicetree/bindings/clock/st,clkgen-mux.txt
-[4] Documentation/devicetree/bindings/clock/st,clkgen-pll.txt
-[7] Documentation/devicetree/bindings/clock/st,quadfs.txt
-[8] Documentation/devicetree/bindings/clock/st,flexgen.txt
+[3] Documentation/devicetree/bindings/clock/st/st,clkgen-mux.txt
+[4] Documentation/devicetree/bindings/clock/st/st,clkgen-pll.txt
+[7] Documentation/devicetree/bindings/clock/st/st,quadfs.txt
+[8] Documentation/devicetree/bindings/clock/st/st,flexgen.txt
Required properties:
that is used.
[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
-[2] Documentation/devicetree/bindings/clock/gate-clock.txt
+[2] Documentation/devicetree/bindings/clock/gpio-gate-clock.txt
[3] Documentation/devicetree/bindings/clock/ti/clockdomain.txt
Required properties:
clock) and hardware autoidle enable / disable.
[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
-[2] Documentation/devicetree/bindings/clock/gate-clock.txt
+[2] Documentation/devicetree/bindings/clock/gpio-gate-clock.txt
Required properties:
- compatible : shall be one of:
"intermediate" - A parent of "cpu" clock which is used as "intermediate" clock
source (usually MAINPLL) when the original CPU PLL is under
transition and not stable yet.
- Please refer to Documentation/devicetree/bindings/clk/clock-bindings.txt for
+ Please refer to Documentation/devicetree/bindings/clock/clock-bindings.txt for
generic clock consumer properties.
- operating-points-v2: Please refer to Documentation/devicetree/bindings/opp/opp.txt
for detail.
- clocks: Phandles for clock specified in "clock-names" property
- clock-names : The name of clock used by the DFI, must be
"pclk_ddr_mon";
-- operating-points-v2: Refer to Documentation/devicetree/bindings/power/opp.txt
+- operating-points-v2: Refer to Documentation/devicetree/bindings/opp/opp.txt
for details.
- center-supply: DMC supply node.
- status: Marks the node enabled/disabled.
- nxp,calib-gpios: calibration GPIO, which must correspond with the
gpio used for the TDA998x interrupt pin.
-[1] Documentation/sound/alsa/soc/DAI.txt
+[1] Documentation/sound/soc/dai.rst
[2] include/dt-bindings/display/tda998x.h
Example:
- mali-supply : Phandle to regulator for the Mali device. Refer to
Documentation/devicetree/bindings/regulator/regulator.txt for details.
-- operating-points-v2 : Refer to Documentation/devicetree/bindings/power/opp.txt
+- operating-points-v2 : Refer to Documentation/devicetree/bindings/opp/opp.txt
for details.
- memory-region:
Memory region to allocate from, as defined in
- Documentation/devicetree/bindi/reserved-memory/reserved-memory.txt
+ Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt
- mali-supply:
Phandle to regulator for the Mali device, as defined in
- clock-frequency : desired I2C bus clock frequency in Hz.
- ti,has-pfunc: boolean; if defined, it indicates that SoC supports PFUNC
registers. PFUNC registers allow to switch I2C pins to function as
- GPIOs, so they can by toggled manually.
+ GPIOs, so they can be toggled manually.
Example (enbw_cmc board):
i2c@1c22000 {
"renesas,i2c-r8a7796" if the device is a part of a R8A7796 SoC.
"renesas,i2c-r8a77965" if the device is a part of a R8A77965 SoC.
"renesas,i2c-r8a77970" if the device is a part of a R8A77970 SoC.
+ "renesas,i2c-r8a77980" if the device is a part of a R8A77980 SoC.
"renesas,i2c-r8a77995" if the device is a part of a R8A77995 SoC.
"renesas,rcar-gen1-i2c" for a generic R-Car Gen1 compatible device.
"renesas,rcar-gen2-i2c" for a generic R-Car Gen2 or RZ/G1 compatible
(b) "samsung, s3c2440-i2c", for i2c compatible with s3c2440 i2c.
(c) "samsung, s3c2440-hdmiphy-i2c", for s3c2440-like i2c used
inside HDMIPHY block found on several samsung SoCs
- (d) "samsung, exynos5440-i2c", for s3c2440-like i2c used
- on EXYNOS5440 which does not need GPIO configuration.
- (e) "samsung, exynos5-sata-phy-i2c", for s3c2440-like i2c used as
+ (d) "samsung, exynos5-sata-phy-i2c", for s3c2440-like i2c used as
a host to SATA PHY controller on an internal bus.
- reg: physical base address of the controller and length of memory mapped
region.
http://www.synaptics.com/sites/default/files/511-000136-01-Rev-E-RMI4-Interfacing-Guide.pdf
Optional Touch Properties:
-Description in Documentation/devicetree/bindings/input/touch
+Description in Documentation/devicetree/bindings/input/touchscreen
- touchscreen-inverted-x
- touchscreen-inverted-y
- touchscreen-swapped-x-y
This property is deprecated. Instead, a 'steps-per-period ' value should
be used, such as "rotary-encoder,steps-per-period = <2>".
-See Documentation/input/rotary-encoder.txt for more information.
+See Documentation/input/devices/rotary-encoder.rst for more information.
Example:
- pinctrl-names : a pinctrl state named tsin%d-serial or tsin%d-parallel (where %d is tsin-num)
must be defined for each tsin child node.
- pinctrl-0 : phandle referencing pin configuration for this tsin configuration
-See: Documentation/devicetree/bindings/pinctrl/pinctrl-binding.txt
+See: Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt
Required properties (tsin (child) node):
Following properties are require if pin control setting is required
at boot.
- pinctrl-names: A pinctrl state named "default" be defined, using the
- bindings in pinctrl/pinctrl-binding.txt.
+ bindings in pinctrl/pinctrl-bindings.txt.
- pinctrl[0...n]: Properties to contain the phandle that refer to
different nodes of pin control settings. These nodes represents
the pin control setting of state 0 to state n. Each of these
It is interfaced to host controller using SPI interface by a proprietary hardware
called PMIC wrapper or pwrap. MT6397/MT6323 MFD is a child device of pwrap.
See the following for pwarp node definitions:
-Documentation/devicetree/bindings/soc/pwrap.txt
+Documentation/devicetree/bindings/soc/mediatek/pwrap.txt
This document describes the binding for MFD device and its sub module.
Optional parameters:
- resets: Phandle to the parent reset controller.
See ../reset/st,stm32-rcc.txt
+- dmas: List of phandle to dma channels that can be used for
+ this timer instance. There may be up to 7 dma channels.
+- dma-names: List of dma names. Must match 'dmas' property. Valid
+ names are: "ch1", "ch2", "ch3", "ch4", "up", "trig",
+ "com".
Optional subnodes:
- pwm: See ../pwm/pwm-stm32.txt
reg = <0>;
};
};
+
+Example with all dmas:
+ timer@40010000 {
+ ...
+ dmas = <&dmamux1 11 0x400 0x0>,
+ <&dmamux1 12 0x400 0x0>,
+ <&dmamux1 13 0x400 0x0>,
+ <&dmamux1 14 0x400 0x0>,
+ <&dmamux1 15 0x400 0x0>,
+ <&dmamux1 16 0x400 0x0>,
+ <&dmamux1 17 0x400 0x0>;
+ dma-names = "ch1", "ch2", "ch3", "ch4", "up", "trig", "com";
+ ...
+ child nodes...
+ };
- reg: The PRCM registers range
The prcm node may contain several subdevices definitions:
- - see Documentation/devicetree/clk/sunxi.txt for clock devices
- - see Documentation/devicetree/reset/allwinner,sunxi-clock-reset.txt for reset
+ - see Documentation/devicetree/bindings/clock/sunxi.txt for clock devices
+ - see Documentation/devicetree/bindings/reset/allwinner,sunxi-clock-reset.txt for reset
controller devices
rest of the gpios (depending on the bus-width property) are the data lines in
no particular order. The format of the gpio specifier depends on the gpio
controller.
-(Deprecated - Refer to Documentation/devicetree/binding/pinctrl/samsung-pinctrl.txt)
+(Deprecated - Refer to Documentation/devicetree/bindings/pinctrl/samsung-pinctrl.txt)
Example:
See: Documentation/devicetree/bindings/clock/clock-bindings.txt
- pinctrl-names: A pinctrl state names "default" must be defined.
- pinctrl-0: Phandle referencing pin configuration of the SDHCI controller.
- See: Documentation/devicetree/bindings/pinctrl/pinctrl-binding.txt
+ See: Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt
Example:
- pinctrl-names: A pinctrl state names "default" must be defined.
- pinctrl-0: Phandle referencing pin configuration of the sd/emmc controller.
- See: Documentation/devicetree/bindings/pinctrl/pinctrl-binding.txt
+ See: Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt
- reg: This must provide the host controller base address and it can also
contain the FlashSS Top register for TX/RX delay used by the driver
- compatible: For external switch chips, compatible string must be exactly one
of: "microchip,ksz9477"
-See Documentation/devicetree/bindings/dsa/dsa.txt for a list of additional
+See Documentation/devicetree/bindings/net/dsa/dsa.txt for a list of additional
required and optional properties.
Examples:
- phy-mode: String, must be either "trgmii" or "rgmii" for port labeled
"cpu".
-See Documentation/devicetree/bindings/dsa/dsa.txt for a list of additional
+See Documentation/devicetree/bindings/net/dsa/dsa.txt for a list of additional
required, optional properties and how the integrated switch subnodes must
be specified.
Data cells:
Data cells are child nodes of eerpom node, bindings for which are
-documented in Documentation/bindings/nvmem/nvmem.txt
+documented in Documentation/devicetree/bindings/nvmem/nvmem.txt
Example:
HiSilicon PCIe host controller is based on the Synopsys DesignWare PCI core.
It shares common functions with the PCIe DesignWare core driver and inherits
common properties defined in
-Documentation/devicetree/bindings/pci/designware-pci.txt.
+Documentation/devicetree/bindings/pci/designware-pcie.txt.
Additional properties are described here:
Kirin PCIe host controller is based on the Synopsys DesignWare PCI core.
It shares common functions with the PCIe DesignWare core driver and
inherits common properties defined in
-Documentation/devicetree/bindings/pci/designware-pci.txt.
+Documentation/devicetree/bindings/pci/designware-pcie.txt.
Additional properties are described here:
Keystone PCI host Controller is based on the Synopsys DesignWare PCI
hardware version 3.65. It shares common functions with the PCIe DesignWare
core driver and inherits common properties defined in
-Documentation/devicetree/bindings/pci/designware-pci.txt
+Documentation/devicetree/bindings/pci/designware-pcie.txt
-Please refer to Documentation/devicetree/bindings/pci/designware-pci.txt
+Please refer to Documentation/devicetree/bindings/pci/designware-pcie.txt
for the details of DesignWare DT bindings. Additional properties are
described here as well as properties that are not applicable.
--------------------------
Following properties are required if default setting of pins are required
at boot.
-- pinctrl-names: A pinctrl state named per <pinctrl-binding.txt>.
+- pinctrl-names: A pinctrl state named per <pinctrl-bindings.txt>.
- pinctrl[0...n]: Properties to contain the phandle for pinctrl states per
- <pinctrl-binding.txt>.
+ <pinctrl-bindings.txt>.
The pin configurations are defined as child of the pinctrl states node. Each
sub-node have following properties:
--------------------------
Following properties are required if default setting of pins are required
at boot.
-- pinctrl-names: A pinctrl state named per <pinctrl-binding.txt>.
+- pinctrl-names: A pinctrl state named per <pinctrl-bindings.txt>.
- pinctrl[0...n]: Properties to contain the phandle for pinctrl states per
- <pinctrl-binding.txt>.
+ <pinctrl-bindings.txt>.
The pin configurations are defined as child of the pinctrl states node. Each
sub-node have following properties:
--------------------------
Following properties are required if default setting of pins are required
at boot.
-- pinctrl-names: A pinctrl state named per <pinctrl-binding.txt>.
+- pinctrl-names: A pinctrl state named per <pinctrl-bindings.txt>.
- pinctrl[0...n]: Properties to contain the phandle for pinctrl states per
- <pinctrl-binding.txt>.
+ <pinctrl-bindings.txt>.
The pin configurations are defined as child of the pinctrl states node. Each
sub-node have following properties:
datasheet
- interrupts: Should contain one interrupt specifier for the GPC interrupt
- clocks: Must contain an entry for each entry in clock-names.
- See Documentation/devicetree/bindings/clocks/clock-bindings.txt for details.
+ See Documentation/devicetree/bindings/clock/clock-bindings.txt for details.
- clock-names: Must include the following entries:
- ipg
};
For information on battery specific node, Ref:
-Documentation/devicetree/bindings/power_supply/ab8500/fg.txt
+Documentation/devicetree/bindings/power/supply/ab8500/fg.txt
};
For information on battery specific node, Ref:
-Documentation/devicetree/bindings/power_supply/ab8500/fg.txt
+Documentation/devicetree/bindings/power/supply/ab8500/fg.txt
};
For information on battery specific node, Ref:
-Documentation/devicetree/bindings/power_supply/ab8500/fg.txt
+Documentation/devicetree/bindings/power/supply/ab8500/fg.txt
3. "has-tpo" Documentation/devicetree/bindings/rtc/rtc-opal.txt
4. "linux,wakeup" Documentation/devicetree/bindings/input/gpio-matrix-keypad.txt
Documentation/devicetree/bindings/mfd/tc3589x.txt
- Documentation/devicetree/bindings/input/ads7846.txt
+ Documentation/devicetree/bindings/input/touchscreen/ads7846.txt
5. "linux,keypad-wakeup" Documentation/devicetree/bindings/input/qcom,pm8xxx-keypad.txt
6. "linux,input-wakeup" Documentation/devicetree/bindings/input/samsung-keypad.txt
7. "nvidia,wakeup-source" Documentation/devicetree/bindings/input/nvidia,tegra20-kbc.txt
See: Documentation/devicetree/bindings/clock/clock-bindings.txt
- pinctrl-names: A pinctrl state names "default" must be defined.
- pinctrl-0: Phandle referencing pin configuration of the UART peripheral.
- See: Documentation/devicetree/bindings/pinctrl/pinctrl-binding.txt
+ See: Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt
Optional properties:
- cts-gpios: CTS pin for UART
See Documentation/devicetree/bindings/dma/stm32-dma.txt.
- dma-names: Identifier for each DMA request line. Must be "tx" and "rx".
- pinctrl-names: should contain only value "default"
- - pinctrl-0: see Documentation/devicetree/bindings/pinctrl/pinctrl-stm32.txt
+ - pinctrl-0: see Documentation/devicetree/bindings/pinctrl/st,stm32-pinctrl.txt
Optional properties:
- resets: Reference to a reset controller asserting the reset controller
"tx": if sai sub-block is configured as playback DAI
"rx": if sai sub-block is configured as capture DAI
- pinctrl-names: should contain only value "default"
- - pinctrl-0: see Documentation/devicetree/bindings/pinctrl/pinctrl-stm32.txt
+ - pinctrl-0: see Documentation/devicetree/bindings/pinctrl/st,stm32-pinctrl.txt
SAI subnodes Optional properties:
- st,sync: specify synchronization mode.
- clocks : Must contain an entry for each name in clock-names
See ../clk/*
- pinctrl-names : Uses "default", can use "sleep" if provided
- See ../pinctrl/pinctrl-binding.txt
+ See ../pinctrl/pinctrl-bindings.txt
Optional properties:
- cs-gpios : List of GPIO chip selects
the node is not important. The content of the node is defined in dwc3.txt.
Phy documentation is provided in the following places:
-Documentation/devicetree/bindings/phy/rockchip,dwc3-usb-phy.txt
+Documentation/devicetree/bindings/phy/qcom-dwc3-usb-phy.txt
Example device nodes:
enum gpiod_flags flags)
For a more detailed description of the con_id parameter in the DeviceTree case
-see Documentation/gpio/board.txt
+see Documentation/driver-api/gpio/board.rst
The flags parameter is used to optionally specify a direction and initial value
for the GPIO. Values can be:
#
# Feature name: stackprotector
-# Kconfig: HAVE_CC_STACKPROTECTOR
+# Kconfig: HAVE_STACKPROTECTOR
# description: arch supports compiler driven stack overflow protection
#
-----------------------
address its connection to the monitor originates from.
wsize=X
- Specify the maximum write size in bytes. By default there is no
- maximum. Ceph will normally size writes based on the file stripe
- size.
+ Specify the maximum write size in bytes. Default: 16 MB.
rsize=X
- Specify the maximum read size in bytes. Default: 64 MB.
+ Specify the maximum read size in bytes. Default: 16 MB.
rasize=X
- Specify the maximum readahead. Default: 8 MB.
+ Specify the maximum readahead size in bytes. Default: 8 MB.
mount_timeout=X
Specify the timeout value for mount (in seconds), in the case
Scott Lovenberg
Pavel Shilovsky (for great work adding SMB2 support, and various SMB3 features)
Aurelien Aptel (for DFS SMB3 work and some key bug fixes)
-Ronnie Sahlberg (for SMB3 xattr work and bug fixes)
+Ronnie Sahlberg (for SMB3 xattr work, bug fixes, and lots of great work on compounding)
Shirish Pargaonkar (for many ACL patches over the years)
Sachin Prabhu (many bug fixes, including for reconnect, copy offload and security)
+Paulo Alcantara
+Long Li (some great work on RDMA, SMB Direct)
Test case and Bug Report contributors
bugs in error paths. Valuable suggestions also have come from Al Viro
and Dave Miller.
-And thanks to the IBM LTC and Power test teams and SuSE testers for
-finding multiple bugs during excellent stress test runs.
+And thanks to the IBM LTC and Power test teams and SuSE and Citrix and RedHat testers for finding multiple bugs during excellent stress test runs.
+See https://wiki.samba.org/index.php/LinuxCIFSKernel for
+more current information.
+
Version 1.62
------------
Add sockopt=TCP_NODELAY mount option. EA (xattr) routines hardened
a) SMB3 (and SMB3.02) missing optional features:
- multichannel (started), integration with RDMA
- - directory leases (improved metadata caching)
- - T10 copy offload (copy chunk, and "Duplicate Extents" ioctl
+ - directory leases (improved metadata caching), started (root dir only)
+ - T10 copy offload ie "ODX" (copy chunk, and "Duplicate Extents" ioctl
currently the only two server side copy mechanisms supported)
b) improved sparse file support
c) Directory entry caching relies on a 1 second timer, rather than
-using Directory Leases
+using Directory Leases, currently only the root file handle is cached longer
d) quota support (needs minor kernel change since quota calls
to make it to network filesystems or deviceless filesystems)
exists. Also better integration with winbind for resolving SID owners
k) Add tools to take advantage of more smb3 specific ioctls and features
+(passthrough ioctl/fsctl for sending various SMB3 fsctls to the server
+is in progress)
l) encrypted file support
secure) CIFS dialect can be disabled in environments that don't need it
and simplify the code.
-u) Finish up SMB3.1.1 dialect support
-
-v) POSIX Extensions for SMB3.1.1
+v) POSIX Extensions for SMB3.1.1 (started, create and mkdir support added
+so far).
KNOWN BUGS
====================================
1) check out max path names and max path name components against various server
types. Try nested symlinks (8 deep). Return max path name in stat -f information
-2) Improve xfstest's cifs enablement and adapt xfstests where needed to test
-cifs better
+2) Improve xfstest's cifs/smb3 enablement and adapt xfstests where needed to test
+cifs/smb3 better
3) Additional performance testing and optimization using iozone and similar -
there are some easy changes that can be done to parallelize sequential writes,
The shunt value in micro-ohms can be set via platform data or device tree at
compile-time or via the shunt_resistor attribute in sysfs at run-time. Please
-refer to the Documentation/devicetree/bindings/i2c/ina2xx.txt for bindings
+refer to the Documentation/devicetree/bindings/hwmon/ina2xx.txt for bindings
if the device tree is used.
Additionally ina226 supports update_interval attribute as described in
- Write Byte/Block.
Registers:
+CPBLTY 0x0 - capability reg.
+ Bits [6:5] - transaction length. b01 - 72B is supported,
+ 36B in other case.
+ Bit 7 - SMBus block read support.
CTRL 0x1 - control reg.
Resets all the registers.
HALF_CYC 0x4 - cycle reg.
i2c-ocores uses the platform bus, so you need to provide a struct
platform_device with the base address and interrupt number. The
dev.platform_data of the device should also point to a struct
-ocores_i2c_platform_data (see linux/i2c-ocores.h) describing the
+ocores_i2c_platform_data (see linux/platform_data/i2c-ocores.h) describing the
distance between registers and the input clock speed.
There is also a possibility to attach a list of i2c_board_info which
the i2c-ocores driver will add to the bus upon creation.
platform_device with the platform_data pointing to a struct
i2c_mux_gpio_platform_data with the I2C adapter number of the master
bus, the number of bus segments to create and the GPIO pins used
-to control it. See include/linux/i2c-mux-gpio.h for details.
+to control it. See include/linux/platform_data/i2c-mux-gpio.h for details.
E.G. something like this for a MUX providing 4 bus segments
controlled through 3 GPIO pins:
-#include <linux/i2c-mux-gpio.h>
+#include <linux/platform_data/i2c-mux-gpio.h>
#include <linux/platform_device.h>
static const unsigned myboard_gpiomux_gpios[] = {
to describe the dependency on the compiler feature is to use "depends on"
followed by a test macro.
-config CC_STACKPROTECTOR
+config STACKPROTECTOR
bool "Stack Protector buffer overflow detection"
depends on $(cc-option,-fstack-protector)
...
See following documents:
- - Documentation/trace/kprobetrace.txt
- - Documentation/trace/events.txt
+ - Documentation/trace/kprobetrace.rst
+ - Documentation/trace/events.rst
- tools/perf/Documentation/perf-probe.txt
0x6021 ALARM: a sensor is too hot
0x6022 ALARM: a sensor is extremely hot
0x6030 System thermal table changed
+0x6032 Thermal Control command set completion (DYTC, Windows)
0x6040 Nvidia Optimus/AC adapter related (TO BE VERIFIED)
0x60C0 X1 Yoga 2016, Tablet mode status changed
+0x60F0 Thermal Transformation changed (GMTS, Windows)
Battery nearly empty alarms are a last resort attempt to get the
operating system to hibernate or shutdown cleanly (0x2313), or shutdown
that will create a signed tag called ``char-misc-4.15-rc1`` based on the
last commit in the ``char-misc-next`` branch, and sign it with your gpg key
-(see :ref:`Documentation/maintainer/configure_git.rst <configuregit>`).
+(see :ref:`Documentation/maintainer/configure-git.rst <configuregit>`).
Linus will only accept pull requests based on a signed tag. Other
maintainers may differ.
transmission and reception of media dependent frames. Due to the
arbitration on the CAN bus the transmission of a low prio CAN-ID
may be delayed by the reception of a high prio CAN frame. To
-reflect the correct [*]_ traffic on the node the loopback of the sent
+reflect the correct [#f1]_ traffic on the node the loopback of the sent
data has to be performed right after a successful transmission. If
the CAN network interface is not capable of performing the loopback for
some reason the SocketCAN core can do this task as a fallback solution.
the RT-SocketCAN group the loopback optionally may be disabled for each
separate socket. See sockopts from the CAN RAW sockets in :ref:`socketcan-raw-sockets`.
-.. [*] you really like to have this when you're running analyser
+.. [#f1] you really like to have this when you're running analyser
tools like 'candump' or 'cansniffer' on the (same) node.
--- /dev/null
+Supporting PMUs on RISC-V platforms
+==========================================
+Alan Kao <alankao@andestech.com>, Mar 2018
+
+Introduction
+------------
+
+As of this writing, perf_event-related features mentioned in The RISC-V ISA
+Privileged Version 1.10 are as follows:
+(please check the manual for more details)
+
+* [m|s]counteren
+* mcycle[h], cycle[h]
+* minstret[h], instret[h]
+* mhpeventx, mhpcounterx[h]
+
+With such function set only, porting perf would require a lot of work, due to
+the lack of the following general architectural performance monitoring features:
+
+* Enabling/Disabling counters
+ Counters are just free-running all the time in our case.
+* Interrupt caused by counter overflow
+ No such feature in the spec.
+* Interrupt indicator
+ It is not possible to have many interrupt ports for all counters, so an
+ interrupt indicator is required for software to tell which counter has
+ just overflowed.
+* Writing to counters
+ There will be an SBI to support this since the kernel cannot modify the
+ counters [1]. Alternatively, some vendor considers to implement
+ hardware-extension for M-S-U model machines to write counters directly.
+
+This document aims to provide developers a quick guide on supporting their
+PMUs in the kernel. The following sections briefly explain perf' mechanism
+and todos.
+
+You may check previous discussions here [1][2]. Also, it might be helpful
+to check the appendix for related kernel structures.
+
+
+1. Initialization
+-----------------
+
+*riscv_pmu* is a global pointer of type *struct riscv_pmu*, which contains
+various methods according to perf's internal convention and PMU-specific
+parameters. One should declare such instance to represent the PMU. By default,
+*riscv_pmu* points to a constant structure *riscv_base_pmu*, which has very
+basic support to a baseline QEMU model.
+
+Then he/she can either assign the instance's pointer to *riscv_pmu* so that
+the minimal and already-implemented logic can be leveraged, or invent his/her
+own *riscv_init_platform_pmu* implementation.
+
+In other words, existing sources of *riscv_base_pmu* merely provide a
+reference implementation. Developers can flexibly decide how many parts they
+can leverage, and in the most extreme case, they can customize every function
+according to their needs.
+
+
+2. Event Initialization
+-----------------------
+
+When a user launches a perf command to monitor some events, it is first
+interpreted by the userspace perf tool into multiple *perf_event_open*
+system calls, and then each of them calls to the body of *event_init*
+member function that was assigned in the previous step. In *riscv_base_pmu*'s
+case, it is *riscv_event_init*.
+
+The main purpose of this function is to translate the event provided by user
+into bitmap, so that HW-related control registers or counters can directly be
+manipulated. The translation is based on the mappings and methods provided in
+*riscv_pmu*.
+
+Note that some features can be done in this stage as well:
+
+(1) interrupt setting, which is stated in the next section;
+(2) privilege level setting (user space only, kernel space only, both);
+(3) destructor setting. Normally it is sufficient to apply *riscv_destroy_event*;
+(4) tweaks for non-sampling events, which will be utilized by functions such as
+*perf_adjust_period*, usually something like the follows:
+
+if (!is_sampling_event(event)) {
+ hwc->sample_period = x86_pmu.max_period;
+ hwc->last_period = hwc->sample_period;
+ local64_set(&hwc->period_left, hwc->sample_period);
+}
+
+In the case of *riscv_base_pmu*, only (3) is provided for now.
+
+
+3. Interrupt
+------------
+
+3.1. Interrupt Initialization
+
+This often occurs at the beginning of the *event_init* method. In common
+practice, this should be a code segment like
+
+int x86_reserve_hardware(void)
+{
+ int err = 0;
+
+ if (!atomic_inc_not_zero(&pmc_refcount)) {
+ mutex_lock(&pmc_reserve_mutex);
+ if (atomic_read(&pmc_refcount) == 0) {
+ if (!reserve_pmc_hardware())
+ err = -EBUSY;
+ else
+ reserve_ds_buffers();
+ }
+ if (!err)
+ atomic_inc(&pmc_refcount);
+ mutex_unlock(&pmc_reserve_mutex);
+ }
+
+ return err;
+}
+
+And the magic is in *reserve_pmc_hardware*, which usually does atomic
+operations to make implemented IRQ accessible from some global function pointer.
+*release_pmc_hardware* serves the opposite purpose, and it is used in event
+destructors mentioned in previous section.
+
+(Note: From the implementations in all the architectures, the *reserve/release*
+pair are always IRQ settings, so the *pmc_hardware* seems somehow misleading.
+It does NOT deal with the binding between an event and a physical counter,
+which will be introduced in the next section.)
+
+3.2. IRQ Structure
+
+Basically, a IRQ runs the following pseudo code:
+
+for each hardware counter that triggered this overflow
+
+ get the event of this counter
+
+ // following two steps are defined as *read()*,
+ // check the section Reading/Writing Counters for details.
+ count the delta value since previous interrupt
+ update the event->count (# event occurs) by adding delta, and
+ event->hw.period_left by subtracting delta
+
+ if the event overflows
+ sample data
+ set the counter appropriately for the next overflow
+
+ if the event overflows again
+ too frequently, throttle this event
+ fi
+ fi
+
+end for
+
+However as of this writing, none of the RISC-V implementations have designed an
+interrupt for perf, so the details are to be completed in the future.
+
+4. Reading/Writing Counters
+---------------------------
+
+They seem symmetric but perf treats them quite differently. For reading, there
+is a *read* interface in *struct pmu*, but it serves more than just reading.
+According to the context, the *read* function not only reads the content of the
+counter (event->count), but also updates the left period to the next interrupt
+(event->hw.period_left).
+
+But the core of perf does not need direct write to counters. Writing counters
+is hidden behind the abstraction of 1) *pmu->start*, literally start counting so one
+has to set the counter to a good value for the next interrupt; 2) inside the IRQ
+it should set the counter to the same resonable value.
+
+Reading is not a problem in RISC-V but writing would need some effort, since
+counters are not allowed to be written by S-mode.
+
+
+5. add()/del()/start()/stop()
+-----------------------------
+
+Basic idea: add()/del() adds/deletes events to/from a PMU, and start()/stop()
+starts/stop the counter of some event in the PMU. All of them take the same
+arguments: *struct perf_event *event* and *int flag*.
+
+Consider perf as a state machine, then you will find that these functions serve
+as the state transition process between those states.
+Three states (event->hw.state) are defined:
+
+* PERF_HES_STOPPED: the counter is stopped
+* PERF_HES_UPTODATE: the event->count is up-to-date
+* PERF_HES_ARCH: arch-dependent usage ... we don't need this for now
+
+A normal flow of these state transitions are as follows:
+
+* A user launches a perf event, resulting in calling to *event_init*.
+* When being context-switched in, *add* is called by the perf core, with a flag
+ PERF_EF_START, which means that the event should be started after it is added.
+ At this stage, a general event is bound to a physical counter, if any.
+ The state changes to PERF_HES_STOPPED and PERF_HES_UPTODATE, because it is now
+ stopped, and the (software) event count does not need updating.
+** *start* is then called, and the counter is enabled.
+ With flag PERF_EF_RELOAD, it writes an appropriate value to the counter (check
+ previous section for detail).
+ Nothing is written if the flag does not contain PERF_EF_RELOAD.
+ The state now is reset to none, because it is neither stopped nor updated
+ (the counting already started)
+* When being context-switched out, *del* is called. It then checks out all the
+ events in the PMU and calls *stop* to update their counts.
+** *stop* is called by *del*
+ and the perf core with flag PERF_EF_UPDATE, and it often shares the same
+ subroutine as *read* with the same logic.
+ The state changes to PERF_HES_STOPPED and PERF_HES_UPTODATE, again.
+
+** Life cycle of these two pairs: *add* and *del* are called repeatedly as
+ tasks switch in-and-out; *start* and *stop* is also called when the perf core
+ needs a quick stop-and-start, for instance, when the interrupt period is being
+ adjusted.
+
+Current implementation is sufficient for now and can be easily extended to
+features in the future.
+
+A. Related Structures
+---------------------
+
+* struct pmu: include/linux/perf_event.h
+* struct riscv_pmu: arch/riscv/include/asm/perf_event.h
+
+ Both structures are designed to be read-only.
+
+ *struct pmu* defines some function pointer interfaces, and most of them take
+*struct perf_event* as a main argument, dealing with perf events according to
+perf's internal state machine (check kernel/events/core.c for details).
+
+ *struct riscv_pmu* defines PMU-specific parameters. The naming follows the
+convention of all other architectures.
+
+* struct perf_event: include/linux/perf_event.h
+* struct hw_perf_event
+
+ The generic structure that represents perf events, and the hardware-related
+details.
+
+* struct riscv_hw_events: arch/riscv/include/asm/perf_event.h
+
+ The structure that holds the status of events, has two fixed members:
+the number of events and the array of the events.
+
+References
+----------
+
+[1] https://github.com/riscv/riscv-linux/pull/124
+[2] https://groups.google.com/a/groups.riscv.org/forum/#!topic/sw-dev/f19TmCNP6yA
of a variable stored on the stack, ultimately writing a controlled value
to the stack frame's stored return address. The most widely used defense
is the presence of a stack canary between the stack variables and the
-return address (``CONFIG_CC_STACKPROTECTOR``), which is verified just before
+return address (``CONFIG_STACKPROTECTOR``), which is verified just before
the function returns. Other defenses include things like shadow stacks.
Stack depth overflow
# common globals
# ==============================================================================
-# The version numbering follows numbering of the specification
-# (Documentation/books/kernel-doc-HOWTO).
__version__ = '1.0'
PY3 = sys.version_info[0] == 3
Details on how to use the generic STM API can be found here [2].
[1]. Documentation/ABI/testing/sysfs-bus-coresight-devices-stm
-[2]. Documentation/trace/stm.txt
+[2]. Documentation/trace/stm.rst
[3]. https://github.com/Linaro/perf-opencsd
1. Introduction
===============
-Tracepoints (see Documentation/trace/tracepoints.txt) can be used
+Tracepoints (see Documentation/trace/tracepoints.rst) can be used
without creating custom kernel modules to register probe functions
using the event tracing infrastructure.
The @buf can also be a glob expression to enable all functions that
match a specific pattern.
-See Filter Commands in :file:`Documentation/trace/ftrace.txt`.
+See Filter Commands in :file:`Documentation/trace/ftrace.rst`.
To just trace the schedule function:
Histogram triggers are special event triggers that can be used to
aggregate trace event data into histograms. For information on
- trace events and event triggers, see Documentation/trace/events.txt.
+ trace events and event triggers, see Documentation/trace/events.rst.
2. Histogram Trigger Command
STH registers an stm class device, through which it provides interface
to userspace and kernelspace software trace sources. See
-Documentation/trace/stm.txt for more information on that.
+Documentation/trace/stm.rst for more information on that.
MSU can be configured to collect trace data into a system memory
buffer, which can later on be read from its device nodes via read() or
1. Introduction
===============
-Tracepoints (see Documentation/trace/tracepoints.txt) can be used without
+Tracepoints (see Documentation/trace/tracepoints.rst) can be used without
creating custom kernel modules to register probe functions using the event
tracing infrastructure.
3.1 System-Wide Event Enabling
------------------------------
-See Documentation/trace/events.txt for a proper description on how events
+See Documentation/trace/events.rst for a proper description on how events
can be enabled system-wide. A short example of enabling all events related
to page allocation would look something like::
3.4 Local Event Enabling
------------------------
-Documentation/trace/ftrace.txt describes how to enable events on a per-thread
+Documentation/trace/ftrace.rst describes how to enable events on a per-thread
basis using set_ftrace_pid.
3.5 Local Event Enablement with PCL
4. Event Filtering
==================
-Documentation/trace/ftrace.txt covers in-depth how to filter events in
+Documentation/trace/ftrace.rst covers in-depth how to filter events in
ftrace. Obviously using grep and awk of trace_pipe is an option as well
as any script reading trace_pipe.
NOTE:
-This is a version of Documentation/HOWTO translated into Japanese.
+This is a version of Documentation/process/howto.rst translated into Japanese.
This document is maintained by Tsugikazu Shibata <tshibata@ab.jp.nec.com>
If you find any difference between this document and the original file or
a problem with the translation, please contact the maintainer of this file.
ています。 カーネルに関して初めての人はここからスタートすると良い
でしょう。
- :ref:`Documentation/Process/changes.rst <changes>`
+ :ref:`Documentation/process/changes.rst <changes>`
このファイルはカーネルをうまく生成(訳注 build )し、走らせるのに最
小限のレベルで必要な数々のソフトウェアパッケージの一覧を示してい
ます。
독특한 행동에 관하여 흔히 있는 오해들과 혼란들을 해소하고 있기
때문이다.
- :ref:`Documentation/process/stable_kernel_rules.rst <stable_kernel_rules>`
+ :ref:`Documentation/process/stable-kernel-rules.rst <stable_kernel_rules>`
이 문서는 안정적인 커널 배포가 이루어지는 규칙을 설명하고 있으며
여러분들이 이러한 배포들 중 하나에 변경을 하길 원한다면
무엇을 해야 하는지를 설명한다.
程序测试的指导,请参阅
Documentation/power/drivers-testing.txt。有关驱动程序电
源管理问题相对全面的概述,请参阅
- Documentation/power/admin-guide/devices.rst。
+ Documentation/driver-api/pm/devices.rst。
管理: 如果一个驱动程序的作者还在进行有效的维护,那么通常除了那
些明显正确且不需要任何检查的补丁以外,其他所有的补丁都会
-Chinese translated version of Documentation/gpio.txt
+Chinese translated version of Documentation/gpio
If you have any comment or update to the content, please contact the
original document maintainer directly. However, if you have a problem
Linus Walleij <linus.walleij@linaro.org>
Chinese maintainer: Fu Wei <tekkamanninja@gmail.com>
---------------------------------------------------------------------
-Documentation/gpio.txt 的中文翻译
+Documentation/gpio 的中文翻译
如果想评论或更新本文的内容,请直接联系原文档的维护者。如果你使用英文
交流有困难的话,也可以向中文版维护者求助。如果本翻译更新不及时或者翻
-Chinese translated version of Documentation/io_orderings.txt
+Chinese translated version of Documentation/io_ordering.txt
If you have any comment or update to the content, please contact the
original document maintainer directly. However, if you have a problem
-Chinese translated version of Documentation/magic-number.txt
+Chinese translated version of Documentation/process/magic-number.rst
If you have any comment or update to the content, please post to LKML directly.
However, if you have problem communicating in English you can also ask the
Chinese maintainer: Jia Wei Wei <harryxiyou@gmail.com>
---------------------------------------------------------------------
-Documentation/magic-number.txt的中文翻译
+Documentation/process/magic-number.rst的中文翻译
如果想评论或更新本文的内容,请直接发信到LKML。如果你使用英文交流有困难的话,也可
以向中文版维护者求助。如果本翻译更新不及时或者翻译存在问题,请联系中文版维护者。
-Chinese translated version of Documentation/video4linux/omap3isp.txt
+Chinese translated version of Documentation/media/v4l-drivers/omap3isp.rst
If you have any comment or update to the content, please contact the
original document maintainer directly. However, if you have a problem
David Cohen <dacohen@gmail.com>
Chinese maintainer: Fu Wei <tekkamanninja@gmail.com>
---------------------------------------------------------------------
-Documentation/video4linux/omap3isp.txt 的中文翻译
+Documentation/media/v4l-drivers/omap3isp.rst 的中文翻译
如果想评论或更新本文的内容,请直接联系原文档的维护者。如果你使用英文
交流有困难的话,也可以向中文版维护者求助。如果本翻译更新不及时或者翻
-Chinese translated version of Documentation/video4linux/v4l2-framework.txt
+Chinese translated version of Documentation/media/media_kapi.rst
If you have any comment or update to the content, please contact the
original document maintainer directly. However, if you have a problem
Maintainer: Mauro Carvalho Chehab <mchehab@kernel.org>
Chinese maintainer: Fu Wei <tekkamanninja@gmail.com>
---------------------------------------------------------------------
-Documentation/video4linux/v4l2-framework.txt 的中文翻译
+Documentation/media/media_kapi.rst 的中文翻译
如果想评论或更新本文的内容,请直接联系原文档的维护者。如果你使用英文
交流有困难的话,也可以向中文版维护者求助。如果本翻译更新不及时或者翻
线性 DMA(videobuf-dma-contig)以及大多用于 USB 设备的用 vmalloc
分配的缓冲(videobuf-vmalloc)。
-请参阅 Documentation/video4linux/videobuf,以获得更多关于 videobuf
+请参阅 Documentation/media/kapi/v4l2-videobuf.rst,以获得更多关于 videobuf
层的使用信息。
v4l2_fh 结构体
F: arch/arm/boot/dts/nuvoton-npcm*
F: include/dt-bindings/clock/nuvoton,npcm7xx-clks.h
F: drivers/*/*npcm*
-F: Documentation/*/*npcm*
+F: Documentation/devicetree/bindings/*/*npcm*
+F: Documentation/devicetree/bindings/*/*/*npcm*
ARM/NUVOTON W90X900 ARM ARCHITECTURE
M: Wan ZongShun <mcuos.com@gmail.com>
L: alsa-devel@alsa-project.org (moderated for non-subscribers)
T: git git://git.alsa-project.org/alsa-kernel.git
S: Maintained
-F: Documentation/sound/alsa/Bt87x.txt
+F: Documentation/sound/cards/bt87x.rst
F: sound/pci/bt87x.c
BT8XXGPIO DRIVER
M: David Woodhouse <dwmw2@infradead.org>
L: keyrings@vger.kernel.org
S: Maintained
-F: Documentation/module-signing.txt
+F: Documentation/admin-guide/module-signing.rst
F: certs/
F: scripts/sign-file.c
F: scripts/extract-cert.c
M: David Lechner <david@lechnology.com>
S: Maintained
F: drivers/gpu/drm/tinydrm/ili9225.c
-F: Documentation/devicetree/bindings/display/ili9225.txt
+F: Documentation/devicetree/bindings/display/ilitek,ili9225.txt
DRM DRIVER FOR INTEL I810 VIDEO CARDS
S: Orphan / Obsolete
M: David Lechner <david@lechnology.com>
S: Maintained
F: drivers/gpu/drm/tinydrm/st7586.c
-F: Documentation/devicetree/bindings/display/st7586.txt
+F: Documentation/devicetree/bindings/display/sitronix,st7586.txt
DRM DRIVER FOR SITRONIX ST7735R PANELS
M: David Lechner <david@lechnology.com>
S: Maintained
F: drivers/gpu/drm/tinydrm/st7735r.c
-F: Documentation/devicetree/bindings/display/st7735r.txt
+F: Documentation/devicetree/bindings/display/sitronix,st7735r.txt
DRM DRIVER FOR TDFX VIDEO CARDS
S: Orphan / Obsolete
F: drivers/gpu/vga/
F: Documentation/devicetree/bindings/display/
F: Documentation/devicetree/bindings/gpu/
-F: Documentation/devicetree/bindings/video/
F: Documentation/gpu/
F: include/drm/
F: include/uapi/drm/
L: dri-devel@lists.freedesktop.org
S: Supported
F: drivers/gpu/drm/atmel-hlcdc/
-F: Documentation/devicetree/bindings/drm/atmel/
+F: Documentation/devicetree/bindings/display/atmel/
T: git git://anongit.freedesktop.org/drm/drm-misc
DRM DRIVERS FOR BRIDGE CHIPS
F: drivers/gpu/drm/fsl-dcu/
F: Documentation/devicetree/bindings/display/fsl,dcu.txt
F: Documentation/devicetree/bindings/display/fsl,tcon.txt
-F: Documentation/devicetree/bindings/display/panel/nec,nl4827hc19_05b.txt
+F: Documentation/devicetree/bindings/display/panel/nec,nl4827hc19-05b.txt
DRM DRIVERS FOR FREESCALE IMX
M: Philipp Zabel <p.zabel@pengutronix.de>
S: Supported
F: drivers/gpu/drm/v3d/
F: include/uapi/drm/v3d_drm.h
-F: Documentation/devicetree/bindings/display/brcm,bcm-v3d.txt
+F: Documentation/devicetree/bindings/gpu/brcm,bcm-v3d.txt
T: git git://anongit.freedesktop.org/drm/drm-misc
DRM DRIVERS FOR VC4
L: netdev@vger.kernel.org
S: Maintained
F: drivers/net/ethernet/freescale/fman
-F: Documentation/devicetree/bindings/powerpc/fsl/fman.txt
+F: Documentation/devicetree/bindings/net/fsl-fman.txt
FREESCALE QORIQ PTP CLOCK DRIVER
M: Yangbo Lu <yangbo.lu@nxp.com>
M: Haavard Skinnemoen <hskinnemoen@gmail.com>
S: Supported
F: drivers/i2c/busses/i2c-gpio.c
-F: include/linux/i2c-gpio.h
+F: include/linux/platform_data/i2c-gpio.h
GENERIC GPIO I2C MULTIPLEXER DRIVER
M: Peter Korsgaard <peter.korsgaard@barco.com>
L: linux-i2c@vger.kernel.org
S: Supported
F: drivers/i2c/muxes/i2c-mux-gpio.c
-F: include/linux/i2c-mux-gpio.h
+F: include/linux/platform_data/i2c-mux-gpio.h
F: Documentation/i2c/muxes/i2c-mux-gpio
GENERIC HDLC (WAN) DRIVERS
S: Maintained
F: mm/hmm*
F: include/linux/hmm*
-F: Documentation/vm/hmm.txt
+F: Documentation/vm/hmm.rst
HOST AP DRIVER
M: Jouni Malinen <j@w1.fi>
M: Peter Rosin <peda@axentia.se>
L: linux-iio@vger.kernel.org
S: Maintained
-F: Documentation/devicetree/bindings/iio/multiplexer/iio-mux.txt
+F: Documentation/devicetree/bindings/iio/multiplexer/io-channel-mux.txt
F: drivers/iio/multiplexer/iio-mux.c
IIO SUBSYSTEM AND DRIVERS
INTEL(R) TRACE HUB
M: Alexander Shishkin <alexander.shishkin@linux.intel.com>
S: Supported
-F: Documentation/trace/intel_th.txt
+F: Documentation/trace/intel_th.rst
F: drivers/hwtracing/intel_th/
INTEL(R) TRUSTED EXECUTION TECHNOLOGY (TXT)
L: linux-iio@vger.kernel.org
S: Maintained
F: drivers/iio/gyro/mpu3050*
-F: Documentation/devicetree/bindings/iio/gyroscope/inv,mpu3050.txt
+F: Documentation/devicetree/bindings/iio/gyroscope/invensense,mpu3050.txt
IOC3 ETHERNET DRIVER
M: Ralf Baechle <ralf@linux-mips.org>
L: linux-hwmon@vger.kernel.org
S: Maintained
F: Documentation/hwmon/max6697
-F: Documentation/devicetree/bindings/i2c/max6697.txt
+F: Documentation/devicetree/bindings/hwmon/max6697.txt
F: drivers/hwmon/max6697.c
F: include/linux/platform_data/max6697.h
M: Martyn Welch <martyn.welch@collabora.co.uk>
S: Maintained
F: drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c
-F: Documentation/devicetree/bindings/video/bridge/megachips-stdpxxxx-ge-b850v3-fw.txt
+F: Documentation/devicetree/bindings/display/bridge/megachips-stdpxxxx-ge-b850v3-fw.txt
MEGARAID SCSI/SAS DRIVERS
M: Kashyap Desai <kashyap.desai@broadcom.com>
MULTIPLEXER SUBSYSTEM
M: Peter Rosin <peda@axentia.se>
S: Maintained
-F: Documentation/ABI/testing/mux/sysfs-class-mux*
+F: Documentation/ABI/testing/sysfs-class-mux*
F: Documentation/devicetree/bindings/mux/
F: include/linux/dt-bindings/mux/
F: include/linux/mux/
M: Marek Vasut <marex@denx.de>
S: Supported
F: drivers/gpu/drm/mxsfb/
-F: Documentation/devicetree/bindings/display/mxsfb-drm.txt
+F: Documentation/devicetree/bindings/display/mxsfb.txt
MYRICOM MYRI-10G 10GbE DRIVER (MYRI10GE)
M: Chris Lee <christopher.lee@cspi.com>
F: drivers/misc/ocxl/
F: include/misc/ocxl*
F: include/uapi/misc/ocxl.h
-F: Documentation/accelerators/ocxl.txt
+F: Documentation/accelerators/ocxl.rst
OMAP AUDIO SUPPORT
M: Peter Ujfalusi <peter.ujfalusi@ti.com>
F: arch/arm/boot/dts/*dra7*
OMAP DISPLAY SUBSYSTEM and FRAMEBUFFER SUPPORT (DSS2)
-M: Tomi Valkeinen <tomi.valkeinen@ti.com>
L: linux-omap@vger.kernel.org
L: linux-fbdev@vger.kernel.org
-S: Maintained
+S: Orphan
F: drivers/video/fbdev/omap2/
F: Documentation/arm/OMAP/DSS
OMAP FRAMEBUFFER SUPPORT
-M: Tomi Valkeinen <tomi.valkeinen@ti.com>
L: linux-fbdev@vger.kernel.org
L: linux-omap@vger.kernel.org
-S: Maintained
+S: Orphan
F: drivers/video/fbdev/omap/
OMAP GENERAL PURPOSE MEMORY CONTROLLER SUPPORT
F: arch/arm/plat-omap/
F: arch/arm/configs/omap1_defconfig
F: drivers/i2c/busses/i2c-omap.c
-F: include/linux/i2c-omap.h
+F: include/linux/platform_data/i2c-omap.h
OMAP2+ SUPPORT
M: Tony Lindgren <tony@atomide.com>
F: drivers/regulator/tps65910-regulator.c
F: drivers/regulator/twl-regulator.c
F: drivers/regulator/twl6030-regulator.c
-F: include/linux/i2c-omap.h
+F: include/linux/platform_data/i2c-omap.h
ONION OMEGA2+ BOARD
M: Harvey Hunt <harveyhuntnexus@gmail.com>
M: Willy Tarreau <willy@haproxy.com>
M: Ksenija Stanojevic <ksenija.stanojevic@gmail.com>
S: Odd Fixes
-F: Documentation/misc-devices/lcd-panel-cgram.txt
+F: Documentation/auxdisplay/lcd-panel-cgram.txt
F: drivers/misc/panel.c
PARALLEL PORT SUBSYSTEM
L: linux-pci@vger.kernel.org
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
S: Maintained
-F: Documentation/devicetree/bindings/pci/controller-generic-pci.txt
+F: Documentation/devicetree/bindings/pci/host-generic-pci.txt
F: drivers/pci/controller/pci-host-common.c
F: drivers/pci/controller/pci-host-generic.c
M: Binghui Wang <wangbinghui@hisilicon.com>
L: linux-pci@vger.kernel.org
S: Maintained
-F: Documentation/devicetree/bindings/pci/pcie-kirin.txt
+F: Documentation/devicetree/bindings/pci/kirin-pcie.txt
F: drivers/pci/controller/dwc/pcie-kirin.c
PCIE DRIVER FOR HISILICON STB
RISC-V ARCHITECTURE
M: Palmer Dabbelt <palmer@sifive.com>
-M: Albert Ou <albert@sifive.com>
+M: Albert Ou <aou@eecs.berkeley.edu>
L: linux-riscv@lists.infradead.org
T: git git://git.kernel.org/pub/scm/linux/kernel/git/palmer/riscv-linux.git
S: Supported
L: linux-samsung-soc@vger.kernel.org
S: Maintained
F: drivers/crypto/exynos-rng.c
-F: Documentation/devicetree/bindings/crypto/samsung,exynos-rng4.txt
+F: Documentation/devicetree/bindings/rng/samsung,exynos4-rng.txt
SAMSUNG EXYNOS TRUE RANDOM NUMBER GENERATOR (TRNG) DRIVER
M: Łukasz Stelmach <l.stelmach@samsung.com>
F: drivers/media/usb/siano/
F: drivers/media/mmc/siano/
+SIFIVE DRIVERS
+M: Palmer Dabbelt <palmer@sifive.com>
+L: linux-riscv@lists.infradead.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/palmer/riscv-linux.git
+S: Supported
+K: sifive
+N: sifive
+
SILEAD TOUCHSCREEN DRIVER
M: Hans de Goede <hdegoede@redhat.com>
L: linux-input@vger.kernel.org
L: alsa-devel@alsa-project.org (moderated for non-subscribers)
T: git git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git
S: Supported
-F: Documentation/sound/alsa/compress_offload.txt
+F: Documentation/sound/designs/compress-offload.rst
F: include/sound/compress_driver.h
F: include/uapi/sound/compress_*
F: sound/core/compress_offload.c
W: http://alsa-project.org/main/index.php/ASoC
S: Supported
F: Documentation/devicetree/bindings/sound/
-F: Documentation/sound/alsa/soc/
+F: Documentation/sound/soc/
F: sound/soc/
F: include/sound/soc*
F: drivers/pwm/pwm-stm32*
F: include/linux/*/stm32-*tim*
F: Documentation/ABI/testing/*timer-stm32
-F: Documentation/devicetree/bindings/*/stm32-*timer
+F: Documentation/devicetree/bindings/*/stm32-*timer*
F: Documentation/devicetree/bindings/pwm/pwm-stm32*
STMMAC ETHERNET DRIVER
M: Alexander Shishkin <alexander.shishkin@linux.intel.com>
S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/ash/stm.git
-F: Documentation/trace/stm.txt
+F: Documentation/trace/stm.rst
F: drivers/hwtracing/stm/
F: include/linux/stm.h
F: include/uapi/linux/stm.h
M: Ingo Molnar <mingo@redhat.com>
T: git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git perf/core
S: Maintained
-F: Documentation/trace/ftrace.txt
+F: Documentation/trace/ftrace.rst
F: arch/*/*/*/ftrace.h
F: arch/*/kernel/ftrace.c
F: include/*/ftrace.h
L: linux-usb@vger.kernel.org
S: Maintained
F: Documentation/ABI/testing/sysfs-class-typec
-F: Documentation/usb/typec.rst
+F: Documentation/driver-api/usb/typec.rst
F: drivers/usb/typec/
F: include/linux/usb/typec.h
USER-MODE LINUX (UML)
M: Jeff Dike <jdike@addtoit.com>
M: Richard Weinberger <richard@nod.at>
-L: user-mode-linux-devel@lists.sourceforge.net
-L: user-mode-linux-user@lists.sourceforge.net
+L: linux-um@lists.infradead.org
W: http://user-mode-linux.sourceforge.net
T: git git://git.kernel.org/pub/scm/linux/kernel/git/rw/uml.git
S: Maintained
F: Documentation/x86/
F: arch/x86/
+X86 ENTRY CODE
+M: Andy Lutomirski <luto@kernel.org>
+L: linux-kernel@vger.kernel.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git x86/asm
+S: Maintained
+F: arch/x86/entry/
+
X86 MCE INFRASTRUCTURE
M: Tony Luck <tony.luck@intel.com>
M: Borislav Petkov <bp@alien8.de>
F: drivers/platform/olpc/
X86 VDSO
-M: Andy Lutomirski <luto@amacapital.net>
+M: Andy Lutomirski <luto@kernel.org>
L: linux-kernel@vger.kernel.org
T: git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git x86/vdso
S: Maintained
M: Henk Vergonet <Henk.Vergonet@gmail.com>
L: usbb2k-api-dev@nongnu.org
S: Maintained
-F: Documentation/input/yealink.rst
+F: Documentation/input/devices/yealink.rst
F: drivers/input/misc/yealink.*
Z8530 DRIVER FOR AX.25
# SPDX-License-Identifier: GPL-2.0
VERSION = 4
-PATCHLEVEL = 17
+PATCHLEVEL = 18
SUBLEVEL = 0
-EXTRAVERSION =
+EXTRAVERSION = -rc1
NAME = Merciless Moray
# *DOCUMENTATION*
endif
stackp-flags-$(CONFIG_CC_HAS_STACKPROTECTOR_NONE) := -fno-stack-protector
-stackp-flags-$(CONFIG_CC_STACKPROTECTOR) := -fstack-protector
-stackp-flags-$(CONFIG_CC_STACKPROTECTOR_STRONG) := -fstack-protector-strong
+stackp-flags-$(CONFIG_STACKPROTECTOR) := -fstack-protector
+stackp-flags-$(CONFIG_STACKPROTECTOR_STRONG) := -fstack-protector-strong
KBUILD_CFLAGS += $(stackp-flags-y)
in terms of Berkeley Packet Filter programs which implement
task-defined system call filtering polices.
- See Documentation/prctl/seccomp_filter.txt for details.
+ See Documentation/userspace-api/seccomp_filter.rst for details.
preferred-plugin-hostcc := $(if-success,[ $(gcc-version) -ge 40800 ],$(HOSTCXX),$(HOSTCC))
in structures. This reduces the performance hit of RANDSTRUCT
at the cost of weakened randomization.
-config HAVE_CC_STACKPROTECTOR
+config HAVE_STACKPROTECTOR
bool
help
An arch should select this symbol if:
config CC_HAS_STACKPROTECTOR_NONE
def_bool $(cc-option,-fno-stack-protector)
-config CC_STACKPROTECTOR
+config STACKPROTECTOR
bool "Stack Protector buffer overflow detection"
- depends on HAVE_CC_STACKPROTECTOR
+ depends on HAVE_STACKPROTECTOR
depends on $(cc-option,-fstack-protector)
default y
help
about 3% of all kernel functions, which increases kernel code size
by about 0.3%.
-config CC_STACKPROTECTOR_STRONG
+config STACKPROTECTOR_STRONG
bool "Strong Stack Protector"
- depends on CC_STACKPROTECTOR
+ depends on STACKPROTECTOR
depends on $(cc-option,-fstack-protector-strong)
default y
help
select ARCH_HAS_DEVMEM_IS_ALLOWED
select ARCH_HAS_ELF_RANDOMIZE
select ARCH_HAS_FORTIFY_SOURCE
+ select ARCH_HAS_KCOV
select ARCH_HAS_PTE_SPECIAL if ARM_LPAE
- select ARCH_HAS_SET_MEMORY
select ARCH_HAS_PHYS_TO_DMA
+ select ARCH_HAS_SET_MEMORY
select ARCH_HAS_STRICT_KERNEL_RWX if MMU && !XIP_KERNEL
select ARCH_HAS_STRICT_MODULE_RWX if MMU
select ARCH_HAS_TICK_BROADCAST if GENERIC_CLOCKEVENTS_BROADCAST
select HAVE_ARCH_TRACEHOOK
select HAVE_ARM_SMCCC if CPU_V7
select HAVE_EBPF_JIT if !CPU_ENDIAN_BE32
- select HAVE_CC_STACKPROTECTOR
select HAVE_CONTEXT_TRACKING
select HAVE_C_RECORDMCOUNT
select HAVE_DEBUG_KMEMLEAK
select HAVE_RCU_TABLE_FREE if (SMP && ARM_LPAE)
select HAVE_REGS_AND_STACK_ACCESS_API
select HAVE_RSEQ
+ select HAVE_STACKPROTECTOR
select HAVE_SYSCALL_TRACEPOINTS
select HAVE_UID16
select HAVE_VIRT_CPU_ACCOUNTING_GEN
will run faster if you say N here.
See also <file:Documentation/x86/i386/IO-APIC.txt>,
- <file:Documentation/nmi_watchdog.txt> and the SMP-HOWTO available at
+ <file:Documentation/lockup-watchdogs.txt> and the SMP-HOWTO available at
<http://tldp.org/HOWTO/SMP-HOWTO.html>.
If you don't know what to do here, say N.
GCOV_PROFILE := n
+# Prevents link failures: __sanitizer_cov_trace_pc() is not linked in.
+KCOV_INSTRUMENT := n
+
#
# Architecture dependencies
#
* Start addresses are inclusive and end addresses are exclusive;
* start addresses should be rounded down, end addresses up.
*
- * See Documentation/cachetlb.txt for more information.
+ * See Documentation/core-api/cachetlb.rst for more information.
* Please note that the implementation of these, and the required
* effects are cache-type (VIVT/VIPT/PIPT) specific.
*
int main(void)
{
DEFINE(TSK_ACTIVE_MM, offsetof(struct task_struct, active_mm));
-#ifdef CONFIG_CC_STACKPROTECTOR
+#ifdef CONFIG_STACKPROTECTOR
DEFINE(TSK_STACK_CANARY, offsetof(struct task_struct, stack_canary));
#endif
BLANK();
ldr r6, [r2, #TI_CPU_DOMAIN]
#endif
switch_tls r1, r4, r5, r3, r7
-#if defined(CONFIG_CC_STACKPROTECTOR) && !defined(CONFIG_SMP)
+#if defined(CONFIG_STACKPROTECTOR) && !defined(CONFIG_SMP)
ldr r7, [r2, #TI_TASK]
ldr r8, =__stack_chk_guard
.if (TSK_STACK_CANARY > IMM12_MASK)
ldr r0, =thread_notify_head
mov r1, #THREAD_NOTIFY_SWITCH
bl atomic_notifier_call_chain
-#if defined(CONFIG_CC_STACKPROTECTOR) && !defined(CONFIG_SMP)
+#if defined(CONFIG_STACKPROTECTOR) && !defined(CONFIG_SMP)
str r7, [r8]
#endif
THUMB( mov ip, r4 )
#include <asm/tls.h>
#include <asm/vdso.h>
-#ifdef CONFIG_CC_STACKPROTECTOR
+#ifdef CONFIG_STACKPROTECTOR
#include <linux/stackprotector.h>
unsigned long __stack_chk_guard __read_mostly;
EXPORT_SYMBOL(__stack_chk_guard);
obj-$(CONFIG_KVM_ARM_HOST) += switch.o
CFLAGS_switch.o += $(CFLAGS_ARMV7VE)
obj-$(CONFIG_KVM_ARM_HOST) += s2-setup.o
+
+# KVM code is run at a different exception code with a different map, so
+# compiler instrumentation that inserts callbacks or checks into the code may
+# cause crashes. Just disable it.
+GCOV_PROFILE := n
+KASAN_SANITIZE := n
+UBSAN_SANITIZE := n
+KCOV_INSTRUMENT := n
#include <linux/gpio/machine.h>
#include <linux/i2c.h>
#include <linux/i2c-algo-bit.h>
-#include <linux/i2c-gpio.h>
+#include <linux/platform_data/i2c-gpio.h>
#include <linux/platform_data/pca953x.h>
#include <linux/mtd/mtd.h>
#include <linux/gpio.h>
#include <linux/gpio_keys.h>
#include <linux/i2c.h>
-#include <linux/i2c-gpio.h>
+#include <linux/platform_data/i2c-gpio.h>
#include <linux/htcpld.h>
#include <linux/leds.h>
#include <linux/spi/spi.h>
#define __ARCH_ARM_MACH_OMAP1_COMMON_H
#include <linux/mtd/mtd.h>
-#include <linux/i2c-omap.h>
+#include <linux/platform_data/i2c-omap.h>
#include <linux/reboot.h>
#include <asm/exception.h>
*/
#include <linux/i2c.h>
-#include <linux/i2c-omap.h>
+#include <linux/platform_data/i2c-omap.h>
#include <mach/mux.h>
#include "soc.h"
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/mfd/twl.h>
-#include <linux/i2c-omap.h>
+#include <linux/platform_data/i2c-omap.h>
#include <linux/reboot.h>
#include <linux/irqchip/irq-omap-intc.h>
* XXX these should be marked initdata for multi-OMAP kernels
*/
-#include <linux/i2c-omap.h>
+#include <linux/platform_data/i2c-omap.h>
#include <linux/omap-dma.h>
#include "omap_hwmod.h"
* XXX these should be marked initdata for multi-OMAP kernels
*/
-#include <linux/i2c-omap.h>
+#include <linux/platform_data/i2c-omap.h>
#include <linux/platform_data/hsmmc-omap.h>
#include <linux/omap-dma.h>
* GNU General Public License for more details.
*/
-#include <linux/i2c-omap.h>
+#include <linux/platform_data/i2c-omap.h>
#include "omap_hwmod.h"
#include "omap_hwmod_common_data.h"
* XXX these should be marked initdata for multi-OMAP kernels
*/
-#include <linux/i2c-omap.h>
+#include <linux/platform_data/i2c-omap.h>
#include <linux/power/smartreflex.h>
#include <linux/platform_data/hsmmc-omap.h>
#include <linux/io.h>
#include <linux/platform_data/hsmmc-omap.h>
#include <linux/power/smartreflex.h>
-#include <linux/i2c-omap.h>
+#include <linux/platform_data/i2c-omap.h>
#include <linux/omap-dma.h>
#include <linux/io.h>
#include <linux/platform_data/hsmmc-omap.h>
#include <linux/power/smartreflex.h>
-#include <linux/i2c-omap.h>
+#include <linux/platform_data/i2c-omap.h>
#include <linux/omap-dma.h>
#include <linux/io.h>
#include <linux/platform_data/hsmmc-omap.h>
#include <linux/power/smartreflex.h>
-#include <linux/i2c-omap.h>
+#include <linux/platform_data/i2c-omap.h>
#include <linux/omap-dma.h>
#include <linux/wm97xx.h>
#include <linux/power_supply.h>
#include <linux/usb/gpio_vbus.h>
-#include <linux/i2c-gpio.h>
+#include <linux/platform_data/i2c-gpio.h>
#include <linux/gpio/machine.h>
#include <asm/mach-types.h>
#include <linux/sched.h>
#include <linux/gpio.h>
#include <linux/jiffies.h>
-#include <linux/i2c-gpio.h>
+#include <linux/platform_data/i2c-gpio.h>
#include <linux/gpio/machine.h>
#include <linux/platform_data/i2c-pxa.h>
#include <linux/serial_8250.h>
#include <linux/input.h>
#include <linux/gpio_keys.h>
#include <linux/leds.h>
-#include <linux/i2c-gpio.h>
+#include <linux/platform_data/i2c-gpio.h>
#include "generic.h"
# Disable gcov profiling for VDSO code
GCOV_PROFILE := n
+# Prevents link failures: __sanitizer_cov_trace_pc() is not linked in.
+KCOV_INSTRUMENT := n
+
# Force dependency
$(obj)/vdso.o : $(obj)/vdso.so
select HAVE_ARM_SMCCC
select HAVE_EBPF_JIT
select HAVE_C_RECORDMCOUNT
- select HAVE_CC_STACKPROTECTOR
select HAVE_CMPXCHG_DOUBLE
select HAVE_CMPXCHG_LOCAL
select HAVE_CONTEXT_TRACKING
select HAVE_PERF_USER_STACK_DUMP
select HAVE_REGS_AND_STACK_ACCESS_API
select HAVE_RCU_TABLE_FREE
+ select HAVE_STACKPROTECTOR
select HAVE_SYSCALL_TRACEPOINTS
select HAVE_KPROBES
select HAVE_KRETPROBES
* Start addresses are inclusive and end addresses are exclusive; start
* addresses should be rounded down, end addresses up.
*
- * See Documentation/cachetlb.txt for more information. Please note that
+ * See Documentation/core-api/cachetlb.rst for more information. Please note that
* the implementation assumes non-aliasing VIPT D-cache and (aliasing)
* VIPT I-cache.
*
#include <asm/processor.h>
#include <asm/stacktrace.h>
-#ifdef CONFIG_CC_STACKPROTECTOR
+#ifdef CONFIG_STACKPROTECTOR
#include <linux/stackprotector.h>
unsigned long __stack_chk_guard __read_mostly;
EXPORT_SYMBOL(__stack_chk_guard);
}
#endif
-static phys_addr_t memory_limit = (phys_addr_t)ULLONG_MAX;
+static phys_addr_t memory_limit = PHYS_ADDR_MAX;
/*
* Limit the memory size that was specified via FDT.
* high up in memory, add back the kernel region that must be accessible
* via the linear mapping.
*/
- if (memory_limit != (phys_addr_t)ULLONG_MAX) {
+ if (memory_limit != PHYS_ADDR_MAX) {
memblock_mem_limit_remove_map(memory_limit);
memblock_add(__pa_symbol(_text), (u64)(_end - _text));
}
*/
static int dump_mem_limit(struct notifier_block *self, unsigned long v, void *p)
{
- if (memory_limit != (phys_addr_t)ULLONG_MAX) {
+ if (memory_limit != PHYS_ADDR_MAX) {
pr_emerg("Memory Limit: %llu MB\n", memory_limit >> 20);
} else {
pr_emerg("Memory Limit: none\n");
/* A handy thing to have if one has the RAM. Declared in head.S */
extern unsigned long empty_zero_page;
-extern unsigned long zero_page_mask;
/*
* The PTE model described here is that of the Hexagon Virtual Machine,
*/
__vmsetvec(_K_VM_event_vector);
- printk(KERN_INFO "PHYS_OFFSET=0x%08x\n", PHYS_OFFSET);
+ printk(KERN_INFO "PHYS_OFFSET=0x%08lx\n", PHYS_OFFSET);
/*
* Simulator has a few differences from the hardware.
/* Set as variable to limit PMD copies */
int max_kernel_seg = 0x303;
-/* think this should be (page_size-1) the way it's used...*/
-unsigned long zero_page_mask;
-
/* indicate pfn's of high memory */
unsigned long highstart_pfn, highend_pfn;
#include <linux/mm.h>
#include <linux/io.h>
-/* Look at Documentation/cachetlb.txt */
+/* Look at Documentation/core-api/cachetlb.rst */
/*
* Cache handling functions.
select HAVE_ARCH_TRANSPARENT_HUGEPAGE if CPU_SUPPORTS_HUGEPAGES && 64BIT
select HAVE_CBPF_JIT if (!64BIT && !CPU_MICROMIPS)
select HAVE_EBPF_JIT if (64BIT && !CPU_MICROMIPS)
- select HAVE_CC_STACKPROTECTOR
select HAVE_CONTEXT_TRACKING
select HAVE_COPY_THREAD_TLS
select HAVE_C_RECORDMCOUNT
select HAVE_OPROFILE
select HAVE_PERF_EVENTS
select HAVE_REGS_AND_STACK_ACCESS_API
+ select HAVE_STACKPROTECTOR
select HAVE_SYSCALL_TRACEPOINTS
select HAVE_VIRT_CPU_ACCOUNTING_GEN if 64BIT || !SMP
select IRQ_FORCED_THREADING
#include <linux/leds.h>
#include <linux/gpio.h>
#include <linux/i2c.h>
-#include <linux/i2c-gpio.h>
+#include <linux/platform_data/i2c-gpio.h>
#include <linux/gpio/machine.h>
#include <asm/bootinfo.h>
#include <asm/idle.h>
OFFSET(TASK_FLAGS, task_struct, flags);
OFFSET(TASK_MM, task_struct, mm);
OFFSET(TASK_PID, task_struct, pid);
-#if defined(CONFIG_CC_STACKPROTECTOR)
+#if defined(CONFIG_STACKPROTECTOR)
OFFSET(TASK_STACK_CANARY, task_struct, stack_canary);
#endif
DEFINE(TASK_STRUCT_SIZE, sizeof(struct task_struct));
#endif
3:
-#if defined(CONFIG_CC_STACKPROTECTOR) && !defined(CONFIG_SMP)
+#if defined(CONFIG_STACKPROTECTOR) && !defined(CONFIG_SMP)
PTR_LA t8, __stack_chk_guard
LONG_L t9, TASK_STACK_CANARY(a1)
LONG_S t9, 0(t8)
return 0;
}
-#ifdef CONFIG_CC_STACKPROTECTOR
+#ifdef CONFIG_STACKPROTECTOR
#include <linux/stackprotector.h>
unsigned long __stack_chk_guard __read_mostly;
EXPORT_SYMBOL(__stack_chk_guard);
cpu_save_nonscratch a0
sw ra, THREAD_REG31(a0)
-#if defined(CONFIG_CC_STACKPROTECTOR) && !defined(CONFIG_SMP)
+#if defined(CONFIG_STACKPROTECTOR) && !defined(CONFIG_SMP)
PTR_LA t8, __stack_chk_guard
LONG_L t9, TASK_STACK_CANARY(a1)
LONG_S t9, 0(t8)
cpu_save_nonscratch a0
LONG_S ra, THREAD_REG31(a0)
-#if defined(CONFIG_CC_STACKPROTECTOR) && !defined(CONFIG_SMP)
+#if defined(CONFIG_STACKPROTECTOR) && !defined(CONFIG_SMP)
PTR_LA t8, __stack_chk_guard
LONG_L t9, TASK_STACK_CANARY(a1)
LONG_S t9, 0(t8)
* If the region reaches the top of the physical address space, adjust
* the size slightly so that (start + size) doesn't overflow
*/
- if (start + size - 1 == (phys_addr_t)ULLONG_MAX)
+ if (start + size - 1 == PHYS_ADDR_MAX)
--size;
/* Sanity check */
unsigned long reserved_end;
unsigned long mapstart = ~0UL;
unsigned long bootmap_size;
- phys_addr_t ramstart = (phys_addr_t)ULLONG_MAX;
+ phys_addr_t ramstart = PHYS_ADDR_MAX;
bool bootmap_valid = false;
int i;
machines, but will use only one CPU of a multiprocessor machine.
On a uniprocessor machine, the kernel will run faster if you say N.
- See also <file:Documentation/nmi_watchdog.txt> and the SMP-HOWTO
+ See also <file:Documentation/lockup-watchdogs.txt> and the SMP-HOWTO
available at <http://www.tldp.org/docs.html#howto>.
If you don't know what to do here, say N.
void pnv_power9_force_smt4_catch(void);
void pnv_power9_force_smt4_release(void);
+/* Transaction memory related */
void tm_enable(void);
void tm_disable(void);
void tm_abort(uint8_t cause);
+
+struct kvm_vcpu;
+void _kvmppc_restore_tm_pr(struct kvm_vcpu *vcpu, u64 guest_msr);
+void _kvmppc_save_tm_pr(struct kvm_vcpu *vcpu, u64 guest_msr);
+
#endif /* _ASM_POWERPC_ASM_PROTOTYPES_H */
ulong vtb; /* virtual timebase */
ulong conferring_threads;
unsigned int halt_poll_ns;
+ atomic_t online_count;
};
struct kvmppc_vcpu_book3s {
extern void kvmppc_book3s_dequeue_irqprio(struct kvm_vcpu *vcpu,
unsigned int vec);
extern void kvmppc_inject_interrupt(struct kvm_vcpu *vcpu, int vec, u64 flags);
+extern void kvmppc_trigger_fac_interrupt(struct kvm_vcpu *vcpu, ulong fac);
extern void kvmppc_set_bat(struct kvm_vcpu *vcpu, struct kvmppc_bat *bat,
bool upper, u32 val);
extern void kvmppc_giveup_ext(struct kvm_vcpu *vcpu, ulong msr);
extern int kvmppc_hcall_impl_hv_realmode(unsigned long cmd);
extern void kvmppc_copy_to_svcpu(struct kvm_vcpu *vcpu);
extern void kvmppc_copy_from_svcpu(struct kvm_vcpu *vcpu);
+
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+void kvmppc_save_tm_pr(struct kvm_vcpu *vcpu);
+void kvmppc_restore_tm_pr(struct kvm_vcpu *vcpu);
+void kvmppc_save_tm_sprs(struct kvm_vcpu *vcpu);
+void kvmppc_restore_tm_sprs(struct kvm_vcpu *vcpu);
+#else
+static inline void kvmppc_save_tm_pr(struct kvm_vcpu *vcpu) {}
+static inline void kvmppc_restore_tm_pr(struct kvm_vcpu *vcpu) {}
+static inline void kvmppc_save_tm_sprs(struct kvm_vcpu *vcpu) {}
+static inline void kvmppc_restore_tm_sprs(struct kvm_vcpu *vcpu) {}
+#endif
+
+void kvmppc_giveup_fac(struct kvm_vcpu *vcpu, ulong fac);
+
extern int kvm_irq_bypass;
static inline struct kvmppc_vcpu_book3s *to_book3s(struct kvm_vcpu *vcpu)
static inline void kvmppc_set_gpr(struct kvm_vcpu *vcpu, int num, ulong val)
{
- vcpu->arch.gpr[num] = val;
+ vcpu->arch.regs.gpr[num] = val;
}
static inline ulong kvmppc_get_gpr(struct kvm_vcpu *vcpu, int num)
{
- return vcpu->arch.gpr[num];
+ return vcpu->arch.regs.gpr[num];
}
static inline void kvmppc_set_cr(struct kvm_vcpu *vcpu, u32 val)
static inline void kvmppc_set_xer(struct kvm_vcpu *vcpu, ulong val)
{
- vcpu->arch.xer = val;
+ vcpu->arch.regs.xer = val;
}
static inline ulong kvmppc_get_xer(struct kvm_vcpu *vcpu)
{
- return vcpu->arch.xer;
+ return vcpu->arch.regs.xer;
}
static inline void kvmppc_set_ctr(struct kvm_vcpu *vcpu, ulong val)
{
- vcpu->arch.ctr = val;
+ vcpu->arch.regs.ctr = val;
}
static inline ulong kvmppc_get_ctr(struct kvm_vcpu *vcpu)
{
- return vcpu->arch.ctr;
+ return vcpu->arch.regs.ctr;
}
static inline void kvmppc_set_lr(struct kvm_vcpu *vcpu, ulong val)
{
- vcpu->arch.lr = val;
+ vcpu->arch.regs.link = val;
}
static inline ulong kvmppc_get_lr(struct kvm_vcpu *vcpu)
{
- return vcpu->arch.lr;
+ return vcpu->arch.regs.link;
}
static inline void kvmppc_set_pc(struct kvm_vcpu *vcpu, ulong val)
{
- vcpu->arch.pc = val;
+ vcpu->arch.regs.nip = val;
}
static inline ulong kvmppc_get_pc(struct kvm_vcpu *vcpu)
{
- return vcpu->arch.pc;
+ return vcpu->arch.regs.nip;
}
static inline u64 kvmppc_get_msr(struct kvm_vcpu *vcpu);
static inline void copy_from_checkpoint(struct kvm_vcpu *vcpu)
{
vcpu->arch.cr = vcpu->arch.cr_tm;
- vcpu->arch.xer = vcpu->arch.xer_tm;
- vcpu->arch.lr = vcpu->arch.lr_tm;
- vcpu->arch.ctr = vcpu->arch.ctr_tm;
+ vcpu->arch.regs.xer = vcpu->arch.xer_tm;
+ vcpu->arch.regs.link = vcpu->arch.lr_tm;
+ vcpu->arch.regs.ctr = vcpu->arch.ctr_tm;
vcpu->arch.amr = vcpu->arch.amr_tm;
vcpu->arch.ppr = vcpu->arch.ppr_tm;
vcpu->arch.dscr = vcpu->arch.dscr_tm;
vcpu->arch.tar = vcpu->arch.tar_tm;
- memcpy(vcpu->arch.gpr, vcpu->arch.gpr_tm,
- sizeof(vcpu->arch.gpr));
+ memcpy(vcpu->arch.regs.gpr, vcpu->arch.gpr_tm,
+ sizeof(vcpu->arch.regs.gpr));
vcpu->arch.fp = vcpu->arch.fp_tm;
vcpu->arch.vr = vcpu->arch.vr_tm;
vcpu->arch.vrsave = vcpu->arch.vrsave_tm;
static inline void copy_to_checkpoint(struct kvm_vcpu *vcpu)
{
vcpu->arch.cr_tm = vcpu->arch.cr;
- vcpu->arch.xer_tm = vcpu->arch.xer;
- vcpu->arch.lr_tm = vcpu->arch.lr;
- vcpu->arch.ctr_tm = vcpu->arch.ctr;
+ vcpu->arch.xer_tm = vcpu->arch.regs.xer;
+ vcpu->arch.lr_tm = vcpu->arch.regs.link;
+ vcpu->arch.ctr_tm = vcpu->arch.regs.ctr;
vcpu->arch.amr_tm = vcpu->arch.amr;
vcpu->arch.ppr_tm = vcpu->arch.ppr;
vcpu->arch.dscr_tm = vcpu->arch.dscr;
vcpu->arch.tar_tm = vcpu->arch.tar;
- memcpy(vcpu->arch.gpr_tm, vcpu->arch.gpr,
- sizeof(vcpu->arch.gpr));
+ memcpy(vcpu->arch.gpr_tm, vcpu->arch.regs.gpr,
+ sizeof(vcpu->arch.regs.gpr));
vcpu->arch.fp_tm = vcpu->arch.fp;
vcpu->arch.vr_tm = vcpu->arch.vr;
vcpu->arch.vrsave_tm = vcpu->arch.vrsave;
static inline void kvmppc_set_gpr(struct kvm_vcpu *vcpu, int num, ulong val)
{
- vcpu->arch.gpr[num] = val;
+ vcpu->arch.regs.gpr[num] = val;
}
static inline ulong kvmppc_get_gpr(struct kvm_vcpu *vcpu, int num)
{
- return vcpu->arch.gpr[num];
+ return vcpu->arch.regs.gpr[num];
}
static inline void kvmppc_set_cr(struct kvm_vcpu *vcpu, u32 val)
static inline void kvmppc_set_xer(struct kvm_vcpu *vcpu, ulong val)
{
- vcpu->arch.xer = val;
+ vcpu->arch.regs.xer = val;
}
static inline ulong kvmppc_get_xer(struct kvm_vcpu *vcpu)
{
- return vcpu->arch.xer;
+ return vcpu->arch.regs.xer;
}
static inline bool kvmppc_need_byteswap(struct kvm_vcpu *vcpu)
static inline void kvmppc_set_ctr(struct kvm_vcpu *vcpu, ulong val)
{
- vcpu->arch.ctr = val;
+ vcpu->arch.regs.ctr = val;
}
static inline ulong kvmppc_get_ctr(struct kvm_vcpu *vcpu)
{
- return vcpu->arch.ctr;
+ return vcpu->arch.regs.ctr;
}
static inline void kvmppc_set_lr(struct kvm_vcpu *vcpu, ulong val)
{
- vcpu->arch.lr = val;
+ vcpu->arch.regs.link = val;
}
static inline ulong kvmppc_get_lr(struct kvm_vcpu *vcpu)
{
- return vcpu->arch.lr;
+ return vcpu->arch.regs.link;
}
static inline void kvmppc_set_pc(struct kvm_vcpu *vcpu, ulong val)
{
- vcpu->arch.pc = val;
+ vcpu->arch.regs.nip = val;
}
static inline ulong kvmppc_get_pc(struct kvm_vcpu *vcpu)
{
- return vcpu->arch.pc;
+ return vcpu->arch.regs.nip;
}
static inline ulong kvmppc_get_fault_dar(struct kvm_vcpu *vcpu)
unsigned long host_lpcr;
unsigned long sdr1;
unsigned long host_sdr1;
- int tlbie_lock;
unsigned long lpcr;
unsigned long vrma_slb_v;
int mmu_ready;
#define KVMPPC_VSX_COPY_WORD 1
#define KVMPPC_VSX_COPY_DWORD 2
#define KVMPPC_VSX_COPY_DWORD_LOAD_DUMP 3
+#define KVMPPC_VSX_COPY_WORD_LOAD_DUMP 4
+
+#define KVMPPC_VMX_COPY_BYTE 8
+#define KVMPPC_VMX_COPY_HWORD 9
+#define KVMPPC_VMX_COPY_WORD 10
+#define KVMPPC_VMX_COPY_DWORD 11
struct openpic;
struct kvmppc_book3s_shadow_vcpu *shadow_vcpu;
#endif
- ulong gpr[32];
+ struct pt_regs regs;
struct thread_fp_state fp;
u32 qpr[32];
#endif
- ulong pc;
- ulong ctr;
- ulong lr;
#ifdef CONFIG_PPC_BOOK3S
ulong tar;
#endif
- ulong xer;
u32 cr;
#ifdef CONFIG_PPC_BOOK3S
struct thread_vr_state vr_tm;
u32 vrsave_tm; /* also USPRG0 */
-
#endif
#ifdef CONFIG_KVM_EXIT_TIMING
* Number of simulations for vsx.
* If we use 2*8bytes to simulate 1*16bytes,
* then the number should be 2 and
- * mmio_vsx_copy_type=KVMPPC_VSX_COPY_DWORD.
+ * mmio_copy_type=KVMPPC_VSX_COPY_DWORD.
* If we use 4*4bytes to simulate 1*16bytes,
* the number should be 4 and
* mmio_vsx_copy_type=KVMPPC_VSX_COPY_WORD.
*/
u8 mmio_vsx_copy_nums;
u8 mmio_vsx_offset;
- u8 mmio_vsx_copy_type;
u8 mmio_vsx_tx_sx_enabled;
u8 mmio_vmx_copy_nums;
+ u8 mmio_vmx_offset;
+ u8 mmio_copy_type;
u8 osi_needed;
u8 osi_enabled;
u8 papr_enabled;
u64 busy_preempt;
u32 emul_inst;
+
+ u32 online;
#endif
#ifdef CONFIG_KVM_BOOK3S_HV_EXIT_TIMING
EMULATE_EXIT_USER, /* emulation requires exit to user-space */
};
-enum instruction_type {
+enum instruction_fetch_type {
INST_GENERIC,
INST_SC, /* system call */
};
extern int kvmppc_handle_vsx_load(struct kvm_run *run, struct kvm_vcpu *vcpu,
unsigned int rt, unsigned int bytes,
int is_default_endian, int mmio_sign_extend);
-extern int kvmppc_handle_load128_by2x64(struct kvm_run *run,
- struct kvm_vcpu *vcpu, unsigned int rt, int is_default_endian);
-extern int kvmppc_handle_store128_by2x64(struct kvm_run *run,
- struct kvm_vcpu *vcpu, unsigned int rs, int is_default_endian);
+extern int kvmppc_handle_vmx_load(struct kvm_run *run, struct kvm_vcpu *vcpu,
+ unsigned int rt, unsigned int bytes, int is_default_endian);
+extern int kvmppc_handle_vmx_store(struct kvm_run *run, struct kvm_vcpu *vcpu,
+ unsigned int rs, unsigned int bytes, int is_default_endian);
extern int kvmppc_handle_store(struct kvm_run *run, struct kvm_vcpu *vcpu,
u64 val, unsigned int bytes,
int is_default_endian);
int is_default_endian);
extern int kvmppc_load_last_inst(struct kvm_vcpu *vcpu,
- enum instruction_type type, u32 *inst);
+ enum instruction_fetch_type type, u32 *inst);
extern int kvmppc_ld(struct kvm_vcpu *vcpu, ulong *eaddr, int size, void *ptr,
bool data);
vector128 vval;
u64 vsxval[2];
u32 vsx32val[4];
+ u16 vsx16val[8];
+ u8 vsx8val[16];
struct {
u64 addr;
u64 length;
int (*get_rmmu_info)(struct kvm *kvm, struct kvm_ppc_rmmu_info *info);
int (*set_smt_mode)(struct kvm *kvm, unsigned long mode,
unsigned long flags);
+ void (*giveup_ext)(struct kvm_vcpu *vcpu, ulong msr);
};
extern struct kvmppc_ops *kvmppc_hv_ops;
extern struct kvmppc_ops *kvmppc_pr_ops;
static inline int kvmppc_get_last_inst(struct kvm_vcpu *vcpu,
- enum instruction_type type, u32 *inst)
+ enum instruction_fetch_type type, u32 *inst)
{
int ret = EMULATE_DONE;
u32 fetched_inst;
#define SPRN_PSSCR 0x357 /* Processor Stop Status and Control Register (ISA 3.0) */
#define SPRN_PSSCR_PR 0x337 /* PSSCR ISA 3.0, privileged mode access */
#define SPRN_PMCR 0x374 /* Power Management Control Register */
+#define SPRN_RWMR 0x375 /* Region-Weighting Mode Register */
/* HFSCR and FSCR bit numbers are the same */
#define FSCR_SCV_LG 12 /* Enable System Call Vectored */
#define KVM_REG_PPC_PSSCR (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xbd)
#define KVM_REG_PPC_DEC_EXPIRY (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xbe)
+#define KVM_REG_PPC_ONLINE (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0xbf)
/* Transactional Memory checkpointed state:
* This is all GPRs, all VSX regs and a subset of SPRs
OFFSET(VCPU_HOST_STACK, kvm_vcpu, arch.host_stack);
OFFSET(VCPU_HOST_PID, kvm_vcpu, arch.host_pid);
OFFSET(VCPU_GUEST_PID, kvm_vcpu, arch.pid);
- OFFSET(VCPU_GPRS, kvm_vcpu, arch.gpr);
+ OFFSET(VCPU_GPRS, kvm_vcpu, arch.regs.gpr);
OFFSET(VCPU_VRSAVE, kvm_vcpu, arch.vrsave);
OFFSET(VCPU_FPRS, kvm_vcpu, arch.fp.fpr);
#ifdef CONFIG_ALTIVEC
OFFSET(VCPU_VRS, kvm_vcpu, arch.vr.vr);
#endif
- OFFSET(VCPU_XER, kvm_vcpu, arch.xer);
- OFFSET(VCPU_CTR, kvm_vcpu, arch.ctr);
- OFFSET(VCPU_LR, kvm_vcpu, arch.lr);
+ OFFSET(VCPU_XER, kvm_vcpu, arch.regs.xer);
+ OFFSET(VCPU_CTR, kvm_vcpu, arch.regs.ctr);
+ OFFSET(VCPU_LR, kvm_vcpu, arch.regs.link);
#ifdef CONFIG_PPC_BOOK3S
OFFSET(VCPU_TAR, kvm_vcpu, arch.tar);
#endif
OFFSET(VCPU_CR, kvm_vcpu, arch.cr);
- OFFSET(VCPU_PC, kvm_vcpu, arch.pc);
+ OFFSET(VCPU_PC, kvm_vcpu, arch.regs.nip);
#ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE
OFFSET(VCPU_MSR, kvm_vcpu, arch.shregs.msr);
OFFSET(VCPU_SRR0, kvm_vcpu, arch.shregs.srr0);
#else /* CONFIG_PPC_BOOK3S */
OFFSET(VCPU_CR, kvm_vcpu, arch.cr);
- OFFSET(VCPU_XER, kvm_vcpu, arch.xer);
- OFFSET(VCPU_LR, kvm_vcpu, arch.lr);
- OFFSET(VCPU_CTR, kvm_vcpu, arch.ctr);
- OFFSET(VCPU_PC, kvm_vcpu, arch.pc);
+ OFFSET(VCPU_XER, kvm_vcpu, arch.regs.xer);
+ OFFSET(VCPU_LR, kvm_vcpu, arch.regs.link);
+ OFFSET(VCPU_CTR, kvm_vcpu, arch.regs.ctr);
+ OFFSET(VCPU_PC, kvm_vcpu, arch.regs.nip);
OFFSET(VCPU_SPRG9, kvm_vcpu, arch.sprg9);
OFFSET(VCPU_LAST_INST, kvm_vcpu, arch.last_inst);
OFFSET(VCPU_FAULT_DEAR, kvm_vcpu, arch.fault_dear);
book3s_64_mmu.o \
book3s_32_mmu.o
+kvm-book3s_64-builtin-objs-$(CONFIG_KVM_BOOK3S_64_HANDLER) += \
+ tm.o
+
ifdef CONFIG_KVM_BOOK3S_PR_POSSIBLE
kvm-book3s_64-builtin-objs-$(CONFIG_KVM_BOOK3S_64_HANDLER) += \
book3s_rmhandlers.o
{
kvmppc_unfixup_split_real(vcpu);
kvmppc_set_srr0(vcpu, kvmppc_get_pc(vcpu));
- kvmppc_set_srr1(vcpu, kvmppc_get_msr(vcpu) | flags);
+ kvmppc_set_srr1(vcpu, (kvmppc_get_msr(vcpu) & ~0x783f0000ul) | flags);
kvmppc_set_pc(vcpu, kvmppc_interrupt_offset(vcpu) + vec);
vcpu->arch.mmu.reset_msr(vcpu);
}
{
kvmppc_set_dar(vcpu, dar);
kvmppc_set_dsisr(vcpu, flags);
- kvmppc_book3s_queue_irqprio(vcpu, BOOK3S_INTERRUPT_DATA_STORAGE);
+ kvmppc_inject_interrupt(vcpu, BOOK3S_INTERRUPT_DATA_STORAGE, 0);
}
-EXPORT_SYMBOL_GPL(kvmppc_core_queue_data_storage); /* used by kvm_hv */
+EXPORT_SYMBOL_GPL(kvmppc_core_queue_data_storage);
void kvmppc_core_queue_inst_storage(struct kvm_vcpu *vcpu, ulong flags)
{
- u64 msr = kvmppc_get_msr(vcpu);
- msr &= ~(SRR1_ISI_NOPT | SRR1_ISI_N_OR_G | SRR1_ISI_PROT);
- msr |= flags & (SRR1_ISI_NOPT | SRR1_ISI_N_OR_G | SRR1_ISI_PROT);
- kvmppc_set_msr_fast(vcpu, msr);
- kvmppc_book3s_queue_irqprio(vcpu, BOOK3S_INTERRUPT_INST_STORAGE);
+ kvmppc_inject_interrupt(vcpu, BOOK3S_INTERRUPT_INST_STORAGE, flags);
}
+EXPORT_SYMBOL_GPL(kvmppc_core_queue_inst_storage);
static int kvmppc_book3s_irqprio_deliver(struct kvm_vcpu *vcpu,
unsigned int priority)
return r;
}
-int kvmppc_load_last_inst(struct kvm_vcpu *vcpu, enum instruction_type type,
- u32 *inst)
+int kvmppc_load_last_inst(struct kvm_vcpu *vcpu,
+ enum instruction_fetch_type type, u32 *inst)
{
ulong pc = kvmppc_get_pc(vcpu);
int r;
{
int i;
- vcpu_load(vcpu);
-
regs->pc = kvmppc_get_pc(vcpu);
regs->cr = kvmppc_get_cr(vcpu);
regs->ctr = kvmppc_get_ctr(vcpu);
for (i = 0; i < ARRAY_SIZE(regs->gpr); i++)
regs->gpr[i] = kvmppc_get_gpr(vcpu, i);
- vcpu_put(vcpu);
return 0;
}
{
int i;
- vcpu_load(vcpu);
-
kvmppc_set_pc(vcpu, regs->pc);
kvmppc_set_cr(vcpu, regs->cr);
kvmppc_set_ctr(vcpu, regs->ctr);
for (i = 0; i < ARRAY_SIZE(regs->gpr); i++)
kvmppc_set_gpr(vcpu, i, regs->gpr[i]);
- vcpu_put(vcpu);
return 0;
}
extern int kvmppc_book3s_init_pr(void);
extern void kvmppc_book3s_exit_pr(void);
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+extern void kvmppc_emulate_tabort(struct kvm_vcpu *vcpu, int ra_val);
+#else
+static inline void kvmppc_emulate_tabort(struct kvm_vcpu *vcpu, int ra_val) {}
+#endif
+
#endif
static inline bool check_debug_ip(struct kvm_vcpu *vcpu)
{
#ifdef DEBUG_MMU_PTE_IP
- return vcpu->arch.pc == DEBUG_MMU_PTE_IP;
+ return vcpu->arch.regs.nip == DEBUG_MMU_PTE_IP;
#else
return true;
#endif
static void kvmppc_mmu_book3s_64_reset_msr(struct kvm_vcpu *vcpu)
{
- kvmppc_set_msr(vcpu, vcpu->arch.intr_msr);
+ unsigned long msr = vcpu->arch.intr_msr;
+ unsigned long cur_msr = kvmppc_get_msr(vcpu);
+
+ /* If transactional, change to suspend mode on IRQ delivery */
+ if (MSR_TM_TRANSACTIONAL(cur_msr))
+ msr |= MSR_TS_S;
+ else
+ msr |= cur_msr & MSR_TS_MASK;
+
+ kvmppc_set_msr(vcpu, msr);
}
static struct kvmppc_slb *kvmppc_mmu_book3s_64_find_slbe(
if (!cpu_has_feature(CPU_FTR_HVMODE))
return -EINVAL;
+ if (!mmu_has_feature(MMU_FTR_LOCKLESS_TLBIE))
+ return -EINVAL;
+
/* POWER7 has 10-bit LPIDs (12-bit in POWER8) */
host_lpid = mfspr(SPRN_LPID);
rsvd_lpid = LPID_RSVD;
return 0;
}
-#ifdef CONFIG_PPC_64K_PAGES
-#define MMU_BASE_PSIZE MMU_PAGE_64K
-#else
-#define MMU_BASE_PSIZE MMU_PAGE_4K
-#endif
-
static void kvmppc_radix_tlbie_page(struct kvm *kvm, unsigned long addr,
unsigned int pshift)
{
- int psize = MMU_BASE_PSIZE;
-
- if (pshift >= PUD_SHIFT)
- psize = MMU_PAGE_1G;
- else if (pshift >= PMD_SHIFT)
- psize = MMU_PAGE_2M;
- addr &= ~0xfffUL;
- addr |= mmu_psize_defs[psize].ap << 5;
- asm volatile("ptesync": : :"memory");
- asm volatile(PPC_TLBIE_5(%0, %1, 0, 0, 1)
- : : "r" (addr), "r" (kvm->arch.lpid) : "memory");
- if (cpu_has_feature(CPU_FTR_P9_TLBIE_BUG))
- asm volatile(PPC_TLBIE_5(%0, %1, 0, 0, 1)
- : : "r" (addr), "r" (kvm->arch.lpid) : "memory");
- asm volatile("eieio ; tlbsync ; ptesync": : :"memory");
+ unsigned long psize = PAGE_SIZE;
+
+ if (pshift)
+ psize = 1UL << pshift;
+
+ addr &= ~(psize - 1);
+ radix__flush_tlb_lpid_page(kvm->arch.lpid, addr, psize);
}
-static void kvmppc_radix_flush_pwc(struct kvm *kvm, unsigned long addr)
+static void kvmppc_radix_flush_pwc(struct kvm *kvm)
{
- unsigned long rb = 0x2 << PPC_BITLSHIFT(53); /* IS = 2 */
-
- asm volatile("ptesync": : :"memory");
- /* RIC=1 PRS=0 R=1 IS=2 */
- asm volatile(PPC_TLBIE_5(%0, %1, 1, 0, 1)
- : : "r" (rb), "r" (kvm->arch.lpid) : "memory");
- asm volatile("eieio ; tlbsync ; ptesync": : :"memory");
+ radix__flush_pwc_lpid(kvm->arch.lpid);
}
-unsigned long kvmppc_radix_update_pte(struct kvm *kvm, pte_t *ptep,
+static unsigned long kvmppc_radix_update_pte(struct kvm *kvm, pte_t *ptep,
unsigned long clr, unsigned long set,
unsigned long addr, unsigned int shift)
{
kmem_cache_free(kvm_pmd_cache, pmdp);
}
+static void kvmppc_unmap_pte(struct kvm *kvm, pte_t *pte,
+ unsigned long gpa, unsigned int shift)
+
+{
+ unsigned long page_size = 1ul << shift;
+ unsigned long old;
+
+ old = kvmppc_radix_update_pte(kvm, pte, ~0UL, 0, gpa, shift);
+ kvmppc_radix_tlbie_page(kvm, gpa, shift);
+ if (old & _PAGE_DIRTY) {
+ unsigned long gfn = gpa >> PAGE_SHIFT;
+ struct kvm_memory_slot *memslot;
+
+ memslot = gfn_to_memslot(kvm, gfn);
+ if (memslot && memslot->dirty_bitmap)
+ kvmppc_update_dirty_map(memslot, gfn, page_size);
+ }
+}
+
+/*
+ * kvmppc_free_p?d are used to free existing page tables, and recursively
+ * descend and clear and free children.
+ * Callers are responsible for flushing the PWC.
+ *
+ * When page tables are being unmapped/freed as part of page fault path
+ * (full == false), ptes are not expected. There is code to unmap them
+ * and emit a warning if encountered, but there may already be data
+ * corruption due to the unexpected mappings.
+ */
+static void kvmppc_unmap_free_pte(struct kvm *kvm, pte_t *pte, bool full)
+{
+ if (full) {
+ memset(pte, 0, sizeof(long) << PTE_INDEX_SIZE);
+ } else {
+ pte_t *p = pte;
+ unsigned long it;
+
+ for (it = 0; it < PTRS_PER_PTE; ++it, ++p) {
+ if (pte_val(*p) == 0)
+ continue;
+ WARN_ON_ONCE(1);
+ kvmppc_unmap_pte(kvm, p,
+ pte_pfn(*p) << PAGE_SHIFT,
+ PAGE_SHIFT);
+ }
+ }
+
+ kvmppc_pte_free(pte);
+}
+
+static void kvmppc_unmap_free_pmd(struct kvm *kvm, pmd_t *pmd, bool full)
+{
+ unsigned long im;
+ pmd_t *p = pmd;
+
+ for (im = 0; im < PTRS_PER_PMD; ++im, ++p) {
+ if (!pmd_present(*p))
+ continue;
+ if (pmd_is_leaf(*p)) {
+ if (full) {
+ pmd_clear(p);
+ } else {
+ WARN_ON_ONCE(1);
+ kvmppc_unmap_pte(kvm, (pte_t *)p,
+ pte_pfn(*(pte_t *)p) << PAGE_SHIFT,
+ PMD_SHIFT);
+ }
+ } else {
+ pte_t *pte;
+
+ pte = pte_offset_map(p, 0);
+ kvmppc_unmap_free_pte(kvm, pte, full);
+ pmd_clear(p);
+ }
+ }
+ kvmppc_pmd_free(pmd);
+}
+
+static void kvmppc_unmap_free_pud(struct kvm *kvm, pud_t *pud)
+{
+ unsigned long iu;
+ pud_t *p = pud;
+
+ for (iu = 0; iu < PTRS_PER_PUD; ++iu, ++p) {
+ if (!pud_present(*p))
+ continue;
+ if (pud_huge(*p)) {
+ pud_clear(p);
+ } else {
+ pmd_t *pmd;
+
+ pmd = pmd_offset(p, 0);
+ kvmppc_unmap_free_pmd(kvm, pmd, true);
+ pud_clear(p);
+ }
+ }
+ pud_free(kvm->mm, pud);
+}
+
+void kvmppc_free_radix(struct kvm *kvm)
+{
+ unsigned long ig;
+ pgd_t *pgd;
+
+ if (!kvm->arch.pgtable)
+ return;
+ pgd = kvm->arch.pgtable;
+ for (ig = 0; ig < PTRS_PER_PGD; ++ig, ++pgd) {
+ pud_t *pud;
+
+ if (!pgd_present(*pgd))
+ continue;
+ pud = pud_offset(pgd, 0);
+ kvmppc_unmap_free_pud(kvm, pud);
+ pgd_clear(pgd);
+ }
+ pgd_free(kvm->mm, kvm->arch.pgtable);
+ kvm->arch.pgtable = NULL;
+}
+
+static void kvmppc_unmap_free_pmd_entry_table(struct kvm *kvm, pmd_t *pmd,
+ unsigned long gpa)
+{
+ pte_t *pte = pte_offset_kernel(pmd, 0);
+
+ /*
+ * Clearing the pmd entry then flushing the PWC ensures that the pte
+ * page no longer be cached by the MMU, so can be freed without
+ * flushing the PWC again.
+ */
+ pmd_clear(pmd);
+ kvmppc_radix_flush_pwc(kvm);
+
+ kvmppc_unmap_free_pte(kvm, pte, false);
+}
+
+static void kvmppc_unmap_free_pud_entry_table(struct kvm *kvm, pud_t *pud,
+ unsigned long gpa)
+{
+ pmd_t *pmd = pmd_offset(pud, 0);
+
+ /*
+ * Clearing the pud entry then flushing the PWC ensures that the pmd
+ * page and any children pte pages will no longer be cached by the MMU,
+ * so can be freed without flushing the PWC again.
+ */
+ pud_clear(pud);
+ kvmppc_radix_flush_pwc(kvm);
+
+ kvmppc_unmap_free_pmd(kvm, pmd, false);
+}
+
+/*
+ * There are a number of bits which may differ between different faults to
+ * the same partition scope entry. RC bits, in the course of cleaning and
+ * aging. And the write bit can change, either the access could have been
+ * upgraded, or a read fault could happen concurrently with a write fault
+ * that sets those bits first.
+ */
+#define PTE_BITS_MUST_MATCH (~(_PAGE_WRITE | _PAGE_DIRTY | _PAGE_ACCESSED))
+
static int kvmppc_create_pte(struct kvm *kvm, pte_t pte, unsigned long gpa,
unsigned int level, unsigned long mmu_seq)
{
pud_t *pud, *new_pud = NULL;
pmd_t *pmd, *new_pmd = NULL;
pte_t *ptep, *new_ptep = NULL;
- unsigned long old;
int ret;
/* Traverse the guest's 2nd-level tree, allocate new levels needed */
if (pud_huge(*pud)) {
unsigned long hgpa = gpa & PUD_MASK;
+ /* Check if we raced and someone else has set the same thing */
+ if (level == 2) {
+ if (pud_raw(*pud) == pte_raw(pte)) {
+ ret = 0;
+ goto out_unlock;
+ }
+ /* Valid 1GB page here already, add our extra bits */
+ WARN_ON_ONCE((pud_val(*pud) ^ pte_val(pte)) &
+ PTE_BITS_MUST_MATCH);
+ kvmppc_radix_update_pte(kvm, (pte_t *)pud,
+ 0, pte_val(pte), hgpa, PUD_SHIFT);
+ ret = 0;
+ goto out_unlock;
+ }
/*
* If we raced with another CPU which has just put
* a 1GB pte in after we saw a pmd page, try again.
*/
- if (level <= 1 && !new_pmd) {
+ if (!new_pmd) {
ret = -EAGAIN;
goto out_unlock;
}
- /* Check if we raced and someone else has set the same thing */
- if (level == 2 && pud_raw(*pud) == pte_raw(pte)) {
- ret = 0;
- goto out_unlock;
- }
/* Valid 1GB page here already, remove it */
- old = kvmppc_radix_update_pte(kvm, (pte_t *)pud,
- ~0UL, 0, hgpa, PUD_SHIFT);
- kvmppc_radix_tlbie_page(kvm, hgpa, PUD_SHIFT);
- if (old & _PAGE_DIRTY) {
- unsigned long gfn = hgpa >> PAGE_SHIFT;
- struct kvm_memory_slot *memslot;
- memslot = gfn_to_memslot(kvm, gfn);
- if (memslot && memslot->dirty_bitmap)
- kvmppc_update_dirty_map(memslot,
- gfn, PUD_SIZE);
- }
+ kvmppc_unmap_pte(kvm, (pte_t *)pud, hgpa, PUD_SHIFT);
}
if (level == 2) {
if (!pud_none(*pud)) {
/*
* There's a page table page here, but we wanted to
* install a large page, so remove and free the page
- * table page. new_pmd will be NULL since level == 2.
+ * table page.
*/
- new_pmd = pmd_offset(pud, 0);
- pud_clear(pud);
- kvmppc_radix_flush_pwc(kvm, gpa);
+ kvmppc_unmap_free_pud_entry_table(kvm, pud, gpa);
}
kvmppc_radix_set_pte_at(kvm, gpa, (pte_t *)pud, pte);
ret = 0;
if (pmd_is_leaf(*pmd)) {
unsigned long lgpa = gpa & PMD_MASK;
+ /* Check if we raced and someone else has set the same thing */
+ if (level == 1) {
+ if (pmd_raw(*pmd) == pte_raw(pte)) {
+ ret = 0;
+ goto out_unlock;
+ }
+ /* Valid 2MB page here already, add our extra bits */
+ WARN_ON_ONCE((pmd_val(*pmd) ^ pte_val(pte)) &
+ PTE_BITS_MUST_MATCH);
+ kvmppc_radix_update_pte(kvm, pmdp_ptep(pmd),
+ 0, pte_val(pte), lgpa, PMD_SHIFT);
+ ret = 0;
+ goto out_unlock;
+ }
+
/*
* If we raced with another CPU which has just put
* a 2MB pte in after we saw a pte page, try again.
*/
- if (level == 0 && !new_ptep) {
+ if (!new_ptep) {
ret = -EAGAIN;
goto out_unlock;
}
- /* Check if we raced and someone else has set the same thing */
- if (level == 1 && pmd_raw(*pmd) == pte_raw(pte)) {
- ret = 0;
- goto out_unlock;
- }
/* Valid 2MB page here already, remove it */
- old = kvmppc_radix_update_pte(kvm, pmdp_ptep(pmd),
- ~0UL, 0, lgpa, PMD_SHIFT);
- kvmppc_radix_tlbie_page(kvm, lgpa, PMD_SHIFT);
- if (old & _PAGE_DIRTY) {
- unsigned long gfn = lgpa >> PAGE_SHIFT;
- struct kvm_memory_slot *memslot;
- memslot = gfn_to_memslot(kvm, gfn);
- if (memslot && memslot->dirty_bitmap)
- kvmppc_update_dirty_map(memslot,
- gfn, PMD_SIZE);
- }
+ kvmppc_unmap_pte(kvm, pmdp_ptep(pmd), lgpa, PMD_SHIFT);
}
if (level == 1) {
if (!pmd_none(*pmd)) {
/*
* There's a page table page here, but we wanted to
* install a large page, so remove and free the page
- * table page. new_ptep will be NULL since level == 1.
+ * table page.
*/
- new_ptep = pte_offset_kernel(pmd, 0);
- pmd_clear(pmd);
- kvmppc_radix_flush_pwc(kvm, gpa);
+ kvmppc_unmap_free_pmd_entry_table(kvm, pmd, gpa);
}
kvmppc_radix_set_pte_at(kvm, gpa, pmdp_ptep(pmd), pte);
ret = 0;
ret = 0;
goto out_unlock;
}
- /* PTE was previously valid, so invalidate it */
- old = kvmppc_radix_update_pte(kvm, ptep, _PAGE_PRESENT,
- 0, gpa, 0);
- kvmppc_radix_tlbie_page(kvm, gpa, 0);
- if (old & _PAGE_DIRTY)
- mark_page_dirty(kvm, gpa >> PAGE_SHIFT);
+ /* Valid page here already, add our extra bits */
+ WARN_ON_ONCE((pte_val(*ptep) ^ pte_val(pte)) &
+ PTE_BITS_MUST_MATCH);
+ kvmppc_radix_update_pte(kvm, ptep, 0, pte_val(pte), gpa, 0);
+ ret = 0;
+ goto out_unlock;
}
kvmppc_radix_set_pte_at(kvm, gpa, ptep, pte);
ret = 0;
unsigned long mask = (1ul << shift) - PAGE_SIZE;
pte = __pte(pte_val(pte) | (hva & mask));
}
- if (!(writing || upgrade_write))
- pte = __pte(pte_val(pte) & ~ _PAGE_WRITE);
- pte = __pte(pte_val(pte) | _PAGE_EXEC);
+ pte = __pte(pte_val(pte) | _PAGE_EXEC | _PAGE_ACCESSED);
+ if (writing || upgrade_write) {
+ if (pte_val(pte) & _PAGE_WRITE)
+ pte = __pte(pte_val(pte) | _PAGE_DIRTY);
+ } else {
+ pte = __pte(pte_val(pte) & ~(_PAGE_WRITE | _PAGE_DIRTY));
+ }
}
/* Allocate space in the tree and write the PTE */
return 0;
}
-void kvmppc_free_radix(struct kvm *kvm)
-{
- unsigned long ig, iu, im;
- pte_t *pte;
- pmd_t *pmd;
- pud_t *pud;
- pgd_t *pgd;
-
- if (!kvm->arch.pgtable)
- return;
- pgd = kvm->arch.pgtable;
- for (ig = 0; ig < PTRS_PER_PGD; ++ig, ++pgd) {
- if (!pgd_present(*pgd))
- continue;
- pud = pud_offset(pgd, 0);
- for (iu = 0; iu < PTRS_PER_PUD; ++iu, ++pud) {
- if (!pud_present(*pud))
- continue;
- if (pud_huge(*pud)) {
- pud_clear(pud);
- continue;
- }
- pmd = pmd_offset(pud, 0);
- for (im = 0; im < PTRS_PER_PMD; ++im, ++pmd) {
- if (pmd_is_leaf(*pmd)) {
- pmd_clear(pmd);
- continue;
- }
- if (!pmd_present(*pmd))
- continue;
- pte = pte_offset_map(pmd, 0);
- memset(pte, 0, sizeof(long) << PTE_INDEX_SIZE);
- kvmppc_pte_free(pte);
- pmd_clear(pmd);
- }
- kvmppc_pmd_free(pmd_offset(pud, 0));
- pud_clear(pud);
- }
- pud_free(kvm->mm, pud_offset(pgd, 0));
- pgd_clear(pgd);
- }
- pgd_free(kvm->mm, kvm->arch.pgtable);
- kvm->arch.pgtable = NULL;
-}
-
static void pte_ctor(void *addr)
{
memset(addr, 0, RADIX_PTE_TABLE_SIZE);
if (!tbltmp)
continue;
- /*
- * Make sure hardware table parameters are exactly the same;
- * this is used in the TCE handlers where boundary checks
- * use only the first attached table.
- */
- if ((tbltmp->it_page_shift == stt->page_shift) &&
- (tbltmp->it_offset == stt->offset) &&
- (tbltmp->it_size == stt->size)) {
+ /* Make sure hardware table parameters are compatible */
+ if ((tbltmp->it_page_shift <= stt->page_shift) &&
+ (tbltmp->it_offset << tbltmp->it_page_shift ==
+ stt->offset << stt->page_shift) &&
+ (tbltmp->it_size << tbltmp->it_page_shift ==
+ stt->size << stt->page_shift)) {
/*
* Reference the table to avoid races with
* add/remove DMA windows.
kfree(stt);
}
-static int kvm_spapr_tce_fault(struct vm_fault *vmf)
+static vm_fault_t kvm_spapr_tce_fault(struct vm_fault *vmf)
{
struct kvmppc_spapr_tce_table *stt = vmf->vma->vm_file->private_data;
struct page *page;
int ret = -ENOMEM;
int i;
- if (!args->size)
+ if (!args->size || args->page_shift < 12 || args->page_shift > 34 ||
+ (args->offset + args->size > (ULLONG_MAX >> args->page_shift)))
return -EINVAL;
size = _ALIGN_UP(args->size, PAGE_SIZE >> 3);
return H_SUCCESS;
}
-static long kvmppc_tce_iommu_unmap(struct kvm *kvm,
+static long kvmppc_tce_iommu_do_unmap(struct kvm *kvm,
struct iommu_table *tbl, unsigned long entry)
{
enum dma_data_direction dir = DMA_NONE;
return ret;
}
-long kvmppc_tce_iommu_map(struct kvm *kvm, struct iommu_table *tbl,
+static long kvmppc_tce_iommu_unmap(struct kvm *kvm,
+ struct kvmppc_spapr_tce_table *stt, struct iommu_table *tbl,
+ unsigned long entry)
+{
+ unsigned long i, ret = H_SUCCESS;
+ unsigned long subpages = 1ULL << (stt->page_shift - tbl->it_page_shift);
+ unsigned long io_entry = entry * subpages;
+
+ for (i = 0; i < subpages; ++i) {
+ ret = kvmppc_tce_iommu_do_unmap(kvm, tbl, io_entry + i);
+ if (ret != H_SUCCESS)
+ break;
+ }
+
+ return ret;
+}
+
+long kvmppc_tce_iommu_do_map(struct kvm *kvm, struct iommu_table *tbl,
unsigned long entry, unsigned long ua,
enum dma_data_direction dir)
{
return 0;
}
+static long kvmppc_tce_iommu_map(struct kvm *kvm,
+ struct kvmppc_spapr_tce_table *stt, struct iommu_table *tbl,
+ unsigned long entry, unsigned long ua,
+ enum dma_data_direction dir)
+{
+ unsigned long i, pgoff, ret = H_SUCCESS;
+ unsigned long subpages = 1ULL << (stt->page_shift - tbl->it_page_shift);
+ unsigned long io_entry = entry * subpages;
+
+ for (i = 0, pgoff = 0; i < subpages;
+ ++i, pgoff += IOMMU_PAGE_SIZE(tbl)) {
+
+ ret = kvmppc_tce_iommu_do_map(kvm, tbl,
+ io_entry + i, ua + pgoff, dir);
+ if (ret != H_SUCCESS)
+ break;
+ }
+
+ return ret;
+}
+
long kvmppc_h_put_tce(struct kvm_vcpu *vcpu, unsigned long liobn,
unsigned long ioba, unsigned long tce)
{
list_for_each_entry_lockless(stit, &stt->iommu_tables, next) {
if (dir == DMA_NONE)
- ret = kvmppc_tce_iommu_unmap(vcpu->kvm,
+ ret = kvmppc_tce_iommu_unmap(vcpu->kvm, stt,
stit->tbl, entry);
else
- ret = kvmppc_tce_iommu_map(vcpu->kvm, stit->tbl,
+ ret = kvmppc_tce_iommu_map(vcpu->kvm, stt, stit->tbl,
entry, ua, dir);
if (ret == H_SUCCESS)
return H_PARAMETER;
list_for_each_entry_lockless(stit, &stt->iommu_tables, next) {
- ret = kvmppc_tce_iommu_map(vcpu->kvm,
+ ret = kvmppc_tce_iommu_map(vcpu->kvm, stt,
stit->tbl, entry + i, ua,
iommu_tce_direction(tce));
return H_PARAMETER;
list_for_each_entry_lockless(stit, &stt->iommu_tables, next) {
- unsigned long entry = ioba >> stit->tbl->it_page_shift;
+ unsigned long entry = ioba >> stt->page_shift;
for (i = 0; i < npages; ++i) {
- ret = kvmppc_tce_iommu_unmap(vcpu->kvm,
+ ret = kvmppc_tce_iommu_unmap(vcpu->kvm, stt,
stit->tbl, entry + i);
if (ret == H_SUCCESS)
return H_SUCCESS;
}
-static long kvmppc_rm_tce_iommu_unmap(struct kvm *kvm,
+static long kvmppc_rm_tce_iommu_do_unmap(struct kvm *kvm,
struct iommu_table *tbl, unsigned long entry)
{
enum dma_data_direction dir = DMA_NONE;
return ret;
}
-static long kvmppc_rm_tce_iommu_map(struct kvm *kvm, struct iommu_table *tbl,
+static long kvmppc_rm_tce_iommu_unmap(struct kvm *kvm,
+ struct kvmppc_spapr_tce_table *stt, struct iommu_table *tbl,
+ unsigned long entry)
+{
+ unsigned long i, ret = H_SUCCESS;
+ unsigned long subpages = 1ULL << (stt->page_shift - tbl->it_page_shift);
+ unsigned long io_entry = entry * subpages;
+
+ for (i = 0; i < subpages; ++i) {
+ ret = kvmppc_rm_tce_iommu_do_unmap(kvm, tbl, io_entry + i);
+ if (ret != H_SUCCESS)
+ break;
+ }
+
+ return ret;
+}
+
+static long kvmppc_rm_tce_iommu_do_map(struct kvm *kvm, struct iommu_table *tbl,
unsigned long entry, unsigned long ua,
enum dma_data_direction dir)
{
return 0;
}
+static long kvmppc_rm_tce_iommu_map(struct kvm *kvm,
+ struct kvmppc_spapr_tce_table *stt, struct iommu_table *tbl,
+ unsigned long entry, unsigned long ua,
+ enum dma_data_direction dir)
+{
+ unsigned long i, pgoff, ret = H_SUCCESS;
+ unsigned long subpages = 1ULL << (stt->page_shift - tbl->it_page_shift);
+ unsigned long io_entry = entry * subpages;
+
+ for (i = 0, pgoff = 0; i < subpages;
+ ++i, pgoff += IOMMU_PAGE_SIZE(tbl)) {
+
+ ret = kvmppc_rm_tce_iommu_do_map(kvm, tbl,
+ io_entry + i, ua + pgoff, dir);
+ if (ret != H_SUCCESS)
+ break;
+ }
+
+ return ret;
+}
+
long kvmppc_rm_h_put_tce(struct kvm_vcpu *vcpu, unsigned long liobn,
unsigned long ioba, unsigned long tce)
{
list_for_each_entry_lockless(stit, &stt->iommu_tables, next) {
if (dir == DMA_NONE)
- ret = kvmppc_rm_tce_iommu_unmap(vcpu->kvm,
+ ret = kvmppc_rm_tce_iommu_unmap(vcpu->kvm, stt,
stit->tbl, entry);
else
- ret = kvmppc_rm_tce_iommu_map(vcpu->kvm,
+ ret = kvmppc_rm_tce_iommu_map(vcpu->kvm, stt,
stit->tbl, entry, ua, dir);
if (ret == H_SUCCESS)
return H_PARAMETER;
list_for_each_entry_lockless(stit, &stt->iommu_tables, next) {
- ret = kvmppc_rm_tce_iommu_map(vcpu->kvm,
+ ret = kvmppc_rm_tce_iommu_map(vcpu->kvm, stt,
stit->tbl, entry + i, ua,
iommu_tce_direction(tce));
return H_PARAMETER;
list_for_each_entry_lockless(stit, &stt->iommu_tables, next) {
- unsigned long entry = ioba >> stit->tbl->it_page_shift;
+ unsigned long entry = ioba >> stt->page_shift;
for (i = 0; i < npages; ++i) {
- ret = kvmppc_rm_tce_iommu_unmap(vcpu->kvm,
+ ret = kvmppc_rm_tce_iommu_unmap(vcpu->kvm, stt,
stit->tbl, entry + i);
if (ret == H_SUCCESS)
page = stt->pages[idx / TCES_PER_PAGE];
tbl = (u64 *)page_address(page);
- vcpu->arch.gpr[4] = tbl[idx % TCES_PER_PAGE];
+ vcpu->arch.regs.gpr[4] = tbl[idx % TCES_PER_PAGE];
return H_SUCCESS;
}
#include <asm/reg.h>
#include <asm/switch_to.h>
#include <asm/time.h>
+#include <asm/tm.h>
#include "book3s.h"
+#include <asm/asm-prototypes.h>
#define OP_19_XOP_RFID 18
#define OP_19_XOP_RFI 50
#define OP_31_XOP_EIOIO 854
#define OP_31_XOP_SLBMFEE 915
+#define OP_31_XOP_TBEGIN 654
+#define OP_31_XOP_TABORT 910
+
+#define OP_31_XOP_TRECLAIM 942
+#define OP_31_XOP_TRCHKPT 1006
+
/* DCBZ is actually 1014, but we patch it to 1010 so we get a trap */
#define OP_31_XOP_DCBZ 1010
return true;
}
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+static inline void kvmppc_copyto_vcpu_tm(struct kvm_vcpu *vcpu)
+{
+ memcpy(&vcpu->arch.gpr_tm[0], &vcpu->arch.regs.gpr[0],
+ sizeof(vcpu->arch.gpr_tm));
+ memcpy(&vcpu->arch.fp_tm, &vcpu->arch.fp,
+ sizeof(struct thread_fp_state));
+ memcpy(&vcpu->arch.vr_tm, &vcpu->arch.vr,
+ sizeof(struct thread_vr_state));
+ vcpu->arch.ppr_tm = vcpu->arch.ppr;
+ vcpu->arch.dscr_tm = vcpu->arch.dscr;
+ vcpu->arch.amr_tm = vcpu->arch.amr;
+ vcpu->arch.ctr_tm = vcpu->arch.regs.ctr;
+ vcpu->arch.tar_tm = vcpu->arch.tar;
+ vcpu->arch.lr_tm = vcpu->arch.regs.link;
+ vcpu->arch.cr_tm = vcpu->arch.cr;
+ vcpu->arch.xer_tm = vcpu->arch.regs.xer;
+ vcpu->arch.vrsave_tm = vcpu->arch.vrsave;
+}
+
+static inline void kvmppc_copyfrom_vcpu_tm(struct kvm_vcpu *vcpu)
+{
+ memcpy(&vcpu->arch.regs.gpr[0], &vcpu->arch.gpr_tm[0],
+ sizeof(vcpu->arch.regs.gpr));
+ memcpy(&vcpu->arch.fp, &vcpu->arch.fp_tm,
+ sizeof(struct thread_fp_state));
+ memcpy(&vcpu->arch.vr, &vcpu->arch.vr_tm,
+ sizeof(struct thread_vr_state));
+ vcpu->arch.ppr = vcpu->arch.ppr_tm;
+ vcpu->arch.dscr = vcpu->arch.dscr_tm;
+ vcpu->arch.amr = vcpu->arch.amr_tm;
+ vcpu->arch.regs.ctr = vcpu->arch.ctr_tm;
+ vcpu->arch.tar = vcpu->arch.tar_tm;
+ vcpu->arch.regs.link = vcpu->arch.lr_tm;
+ vcpu->arch.cr = vcpu->arch.cr_tm;
+ vcpu->arch.regs.xer = vcpu->arch.xer_tm;
+ vcpu->arch.vrsave = vcpu->arch.vrsave_tm;
+}
+
+static void kvmppc_emulate_treclaim(struct kvm_vcpu *vcpu, int ra_val)
+{
+ unsigned long guest_msr = kvmppc_get_msr(vcpu);
+ int fc_val = ra_val ? ra_val : 1;
+ uint64_t texasr;
+
+ /* CR0 = 0 | MSR[TS] | 0 */
+ vcpu->arch.cr = (vcpu->arch.cr & ~(CR0_MASK << CR0_SHIFT)) |
+ (((guest_msr & MSR_TS_MASK) >> (MSR_TS_S_LG - 1))
+ << CR0_SHIFT);
+
+ preempt_disable();
+ tm_enable();
+ texasr = mfspr(SPRN_TEXASR);
+ kvmppc_save_tm_pr(vcpu);
+ kvmppc_copyfrom_vcpu_tm(vcpu);
+
+ /* failure recording depends on Failure Summary bit */
+ if (!(texasr & TEXASR_FS)) {
+ texasr &= ~TEXASR_FC;
+ texasr |= ((u64)fc_val << TEXASR_FC_LG) | TEXASR_FS;
+
+ texasr &= ~(TEXASR_PR | TEXASR_HV);
+ if (kvmppc_get_msr(vcpu) & MSR_PR)
+ texasr |= TEXASR_PR;
+
+ if (kvmppc_get_msr(vcpu) & MSR_HV)
+ texasr |= TEXASR_HV;
+
+ vcpu->arch.texasr = texasr;
+ vcpu->arch.tfiar = kvmppc_get_pc(vcpu);
+ mtspr(SPRN_TEXASR, texasr);
+ mtspr(SPRN_TFIAR, vcpu->arch.tfiar);
+ }
+ tm_disable();
+ /*
+ * treclaim need quit to non-transactional state.
+ */
+ guest_msr &= ~(MSR_TS_MASK);
+ kvmppc_set_msr(vcpu, guest_msr);
+ preempt_enable();
+
+ if (vcpu->arch.shadow_fscr & FSCR_TAR)
+ mtspr(SPRN_TAR, vcpu->arch.tar);
+}
+
+static void kvmppc_emulate_trchkpt(struct kvm_vcpu *vcpu)
+{
+ unsigned long guest_msr = kvmppc_get_msr(vcpu);
+
+ preempt_disable();
+ /*
+ * need flush FP/VEC/VSX to vcpu save area before
+ * copy.
+ */
+ kvmppc_giveup_ext(vcpu, MSR_VSX);
+ kvmppc_giveup_fac(vcpu, FSCR_TAR_LG);
+ kvmppc_copyto_vcpu_tm(vcpu);
+ kvmppc_save_tm_sprs(vcpu);
+
+ /*
+ * as a result of trecheckpoint. set TS to suspended.
+ */
+ guest_msr &= ~(MSR_TS_MASK);
+ guest_msr |= MSR_TS_S;
+ kvmppc_set_msr(vcpu, guest_msr);
+ kvmppc_restore_tm_pr(vcpu);
+ preempt_enable();
+}
+
+/* emulate tabort. at guest privilege state */
+void kvmppc_emulate_tabort(struct kvm_vcpu *vcpu, int ra_val)
+{
+ /* currently we only emulate tabort. but no emulation of other
+ * tabort variants since there is no kernel usage of them at
+ * present.
+ */
+ unsigned long guest_msr = kvmppc_get_msr(vcpu);
+ uint64_t org_texasr;
+
+ preempt_disable();
+ tm_enable();
+ org_texasr = mfspr(SPRN_TEXASR);
+ tm_abort(ra_val);
+
+ /* CR0 = 0 | MSR[TS] | 0 */
+ vcpu->arch.cr = (vcpu->arch.cr & ~(CR0_MASK << CR0_SHIFT)) |
+ (((guest_msr & MSR_TS_MASK) >> (MSR_TS_S_LG - 1))
+ << CR0_SHIFT);
+
+ vcpu->arch.texasr = mfspr(SPRN_TEXASR);
+ /* failure recording depends on Failure Summary bit,
+ * and tabort will be treated as nops in non-transactional
+ * state.
+ */
+ if (!(org_texasr & TEXASR_FS) &&
+ MSR_TM_ACTIVE(guest_msr)) {
+ vcpu->arch.texasr &= ~(TEXASR_PR | TEXASR_HV);
+ if (guest_msr & MSR_PR)
+ vcpu->arch.texasr |= TEXASR_PR;
+
+ if (guest_msr & MSR_HV)
+ vcpu->arch.texasr |= TEXASR_HV;
+
+ vcpu->arch.tfiar = kvmppc_get_pc(vcpu);
+ }
+ tm_disable();
+ preempt_enable();
+}
+
+#endif
+
int kvmppc_core_emulate_op_pr(struct kvm_run *run, struct kvm_vcpu *vcpu,
unsigned int inst, int *advance)
{
case 19:
switch (get_xop(inst)) {
case OP_19_XOP_RFID:
- case OP_19_XOP_RFI:
+ case OP_19_XOP_RFI: {
+ unsigned long srr1 = kvmppc_get_srr1(vcpu);
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+ unsigned long cur_msr = kvmppc_get_msr(vcpu);
+
+ /*
+ * add rules to fit in ISA specification regarding TM
+ * state transistion in TM disable/Suspended state,
+ * and target TM state is TM inactive(00) state. (the
+ * change should be suppressed).
+ */
+ if (((cur_msr & MSR_TM) == 0) &&
+ ((srr1 & MSR_TM) == 0) &&
+ MSR_TM_SUSPENDED(cur_msr) &&
+ !MSR_TM_ACTIVE(srr1))
+ srr1 |= MSR_TS_S;
+#endif
kvmppc_set_pc(vcpu, kvmppc_get_srr0(vcpu));
- kvmppc_set_msr(vcpu, kvmppc_get_srr1(vcpu));
+ kvmppc_set_msr(vcpu, srr1);
*advance = 0;
break;
+ }
default:
emulated = EMULATE_FAIL;
break;
}
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+ case OP_31_XOP_TBEGIN:
+ {
+ if (!cpu_has_feature(CPU_FTR_TM))
+ break;
+
+ if (!(kvmppc_get_msr(vcpu) & MSR_TM)) {
+ kvmppc_trigger_fac_interrupt(vcpu, FSCR_TM_LG);
+ emulated = EMULATE_AGAIN;
+ break;
+ }
+
+ if (!(kvmppc_get_msr(vcpu) & MSR_PR)) {
+ preempt_disable();
+ vcpu->arch.cr = (CR0_TBEGIN_FAILURE |
+ (vcpu->arch.cr & ~(CR0_MASK << CR0_SHIFT)));
+
+ vcpu->arch.texasr = (TEXASR_FS | TEXASR_EXACT |
+ (((u64)(TM_CAUSE_EMULATE | TM_CAUSE_PERSISTENT))
+ << TEXASR_FC_LG));
+
+ if ((inst >> 21) & 0x1)
+ vcpu->arch.texasr |= TEXASR_ROT;
+
+ if (kvmppc_get_msr(vcpu) & MSR_HV)
+ vcpu->arch.texasr |= TEXASR_HV;
+
+ vcpu->arch.tfhar = kvmppc_get_pc(vcpu) + 4;
+ vcpu->arch.tfiar = kvmppc_get_pc(vcpu);
+
+ kvmppc_restore_tm_sprs(vcpu);
+ preempt_enable();
+ } else
+ emulated = EMULATE_FAIL;
+ break;
+ }
+ case OP_31_XOP_TABORT:
+ {
+ ulong guest_msr = kvmppc_get_msr(vcpu);
+ unsigned long ra_val = 0;
+
+ if (!cpu_has_feature(CPU_FTR_TM))
+ break;
+
+ if (!(kvmppc_get_msr(vcpu) & MSR_TM)) {
+ kvmppc_trigger_fac_interrupt(vcpu, FSCR_TM_LG);
+ emulated = EMULATE_AGAIN;
+ break;
+ }
+
+ /* only emulate for privilege guest, since problem state
+ * guest can run with TM enabled and we don't expect to
+ * trap at here for that case.
+ */
+ WARN_ON(guest_msr & MSR_PR);
+
+ if (ra)
+ ra_val = kvmppc_get_gpr(vcpu, ra);
+
+ kvmppc_emulate_tabort(vcpu, ra_val);
+ break;
+ }
+ case OP_31_XOP_TRECLAIM:
+ {
+ ulong guest_msr = kvmppc_get_msr(vcpu);
+ unsigned long ra_val = 0;
+
+ if (!cpu_has_feature(CPU_FTR_TM))
+ break;
+
+ if (!(kvmppc_get_msr(vcpu) & MSR_TM)) {
+ kvmppc_trigger_fac_interrupt(vcpu, FSCR_TM_LG);
+ emulated = EMULATE_AGAIN;
+ break;
+ }
+
+ /* generate interrupts based on priorities */
+ if (guest_msr & MSR_PR) {
+ /* Privileged Instruction type Program Interrupt */
+ kvmppc_core_queue_program(vcpu, SRR1_PROGPRIV);
+ emulated = EMULATE_AGAIN;
+ break;
+ }
+
+ if (!MSR_TM_ACTIVE(guest_msr)) {
+ /* TM bad thing interrupt */
+ kvmppc_core_queue_program(vcpu, SRR1_PROGTM);
+ emulated = EMULATE_AGAIN;
+ break;
+ }
+
+ if (ra)
+ ra_val = kvmppc_get_gpr(vcpu, ra);
+ kvmppc_emulate_treclaim(vcpu, ra_val);
+ break;
+ }
+ case OP_31_XOP_TRCHKPT:
+ {
+ ulong guest_msr = kvmppc_get_msr(vcpu);
+ unsigned long texasr;
+
+ if (!cpu_has_feature(CPU_FTR_TM))
+ break;
+
+ if (!(kvmppc_get_msr(vcpu) & MSR_TM)) {
+ kvmppc_trigger_fac_interrupt(vcpu, FSCR_TM_LG);
+ emulated = EMULATE_AGAIN;
+ break;
+ }
+
+ /* generate interrupt based on priorities */
+ if (guest_msr & MSR_PR) {
+ /* Privileged Instruction type Program Intr */
+ kvmppc_core_queue_program(vcpu, SRR1_PROGPRIV);
+ emulated = EMULATE_AGAIN;
+ break;
+ }
+
+ tm_enable();
+ texasr = mfspr(SPRN_TEXASR);
+ tm_disable();
+
+ if (MSR_TM_ACTIVE(guest_msr) ||
+ !(texasr & (TEXASR_FS))) {
+ /* TM bad thing interrupt */
+ kvmppc_core_queue_program(vcpu, SRR1_PROGTM);
+ emulated = EMULATE_AGAIN;
+ break;
+ }
+
+ kvmppc_emulate_trchkpt(vcpu);
+ break;
+ }
+#endif
default:
emulated = EMULATE_FAIL;
}
break;
#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
case SPRN_TFHAR:
- vcpu->arch.tfhar = spr_val;
- break;
case SPRN_TEXASR:
- vcpu->arch.texasr = spr_val;
- break;
case SPRN_TFIAR:
- vcpu->arch.tfiar = spr_val;
+ if (!cpu_has_feature(CPU_FTR_TM))
+ break;
+
+ if (!(kvmppc_get_msr(vcpu) & MSR_TM)) {
+ kvmppc_trigger_fac_interrupt(vcpu, FSCR_TM_LG);
+ emulated = EMULATE_AGAIN;
+ break;
+ }
+
+ if (MSR_TM_ACTIVE(kvmppc_get_msr(vcpu)) &&
+ !((MSR_TM_SUSPENDED(kvmppc_get_msr(vcpu))) &&
+ (sprn == SPRN_TFHAR))) {
+ /* it is illegal to mtspr() TM regs in
+ * other than non-transactional state, with
+ * the exception of TFHAR in suspend state.
+ */
+ kvmppc_core_queue_program(vcpu, SRR1_PROGTM);
+ emulated = EMULATE_AGAIN;
+ break;
+ }
+
+ tm_enable();
+ if (sprn == SPRN_TFHAR)
+ mtspr(SPRN_TFHAR, spr_val);
+ else if (sprn == SPRN_TEXASR)
+ mtspr(SPRN_TEXASR, spr_val);
+ else
+ mtspr(SPRN_TFIAR, spr_val);
+ tm_disable();
+
break;
#endif
#endif
break;
#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
case SPRN_TFHAR:
- *spr_val = vcpu->arch.tfhar;
- break;
case SPRN_TEXASR:
- *spr_val = vcpu->arch.texasr;
- break;
case SPRN_TFIAR:
- *spr_val = vcpu->arch.tfiar;
+ if (!cpu_has_feature(CPU_FTR_TM))
+ break;
+
+ if (!(kvmppc_get_msr(vcpu) & MSR_TM)) {
+ kvmppc_trigger_fac_interrupt(vcpu, FSCR_TM_LG);
+ emulated = EMULATE_AGAIN;
+ break;
+ }
+
+ tm_enable();
+ if (sprn == SPRN_TFHAR)
+ *spr_val = mfspr(SPRN_TFHAR);
+ else if (sprn == SPRN_TEXASR)
+ *spr_val = mfspr(SPRN_TEXASR);
+ else if (sprn == SPRN_TFIAR)
+ *spr_val = mfspr(SPRN_TFIAR);
+ tm_disable();
break;
#endif
#endif
static void kvmppc_end_cede(struct kvm_vcpu *vcpu);
static int kvmppc_hv_setup_htab_rma(struct kvm_vcpu *vcpu);
+/*
+ * RWMR values for POWER8. These control the rate at which PURR
+ * and SPURR count and should be set according to the number of
+ * online threads in the vcore being run.
+ */
+#define RWMR_RPA_P8_1THREAD 0x164520C62609AECA
+#define RWMR_RPA_P8_2THREAD 0x7FFF2908450D8DA9
+#define RWMR_RPA_P8_3THREAD 0x164520C62609AECA
+#define RWMR_RPA_P8_4THREAD 0x199A421245058DA9
+#define RWMR_RPA_P8_5THREAD 0x164520C62609AECA
+#define RWMR_RPA_P8_6THREAD 0x164520C62609AECA
+#define RWMR_RPA_P8_7THREAD 0x164520C62609AECA
+#define RWMR_RPA_P8_8THREAD 0x164520C62609AECA
+
+static unsigned long p8_rwmr_values[MAX_SMT_THREADS + 1] = {
+ RWMR_RPA_P8_1THREAD,
+ RWMR_RPA_P8_1THREAD,
+ RWMR_RPA_P8_2THREAD,
+ RWMR_RPA_P8_3THREAD,
+ RWMR_RPA_P8_4THREAD,
+ RWMR_RPA_P8_5THREAD,
+ RWMR_RPA_P8_6THREAD,
+ RWMR_RPA_P8_7THREAD,
+ RWMR_RPA_P8_8THREAD,
+};
+
static inline struct kvm_vcpu *next_runnable_thread(struct kvmppc_vcore *vc,
int *ip)
{
pr_err("vcpu %p (%d):\n", vcpu, vcpu->vcpu_id);
pr_err("pc = %.16lx msr = %.16llx trap = %x\n",
- vcpu->arch.pc, vcpu->arch.shregs.msr, vcpu->arch.trap);
+ vcpu->arch.regs.nip, vcpu->arch.shregs.msr, vcpu->arch.trap);
for (r = 0; r < 16; ++r)
pr_err("r%2d = %.16lx r%d = %.16lx\n",
r, kvmppc_get_gpr(vcpu, r),
r+16, kvmppc_get_gpr(vcpu, r+16));
pr_err("ctr = %.16lx lr = %.16lx\n",
- vcpu->arch.ctr, vcpu->arch.lr);
+ vcpu->arch.regs.ctr, vcpu->arch.regs.link);
pr_err("srr0 = %.16llx srr1 = %.16llx\n",
vcpu->arch.shregs.srr0, vcpu->arch.shregs.srr1);
pr_err("sprg0 = %.16llx sprg1 = %.16llx\n",
pr_err("sprg2 = %.16llx sprg3 = %.16llx\n",
vcpu->arch.shregs.sprg2, vcpu->arch.shregs.sprg3);
pr_err("cr = %.8x xer = %.16lx dsisr = %.8x\n",
- vcpu->arch.cr, vcpu->arch.xer, vcpu->arch.shregs.dsisr);
+ vcpu->arch.cr, vcpu->arch.regs.xer, vcpu->arch.shregs.dsisr);
pr_err("dar = %.16llx\n", vcpu->arch.shregs.dar);
pr_err("fault dar = %.16lx dsisr = %.8x\n",
vcpu->arch.fault_dar, vcpu->arch.fault_dsisr);
*val = get_reg_val(id, vcpu->arch.dec_expires +
vcpu->arch.vcore->tb_offset);
break;
+ case KVM_REG_PPC_ONLINE:
+ *val = get_reg_val(id, vcpu->arch.online);
+ break;
default:
r = -EINVAL;
break;
vcpu->arch.dec_expires = set_reg_val(id, *val) -
vcpu->arch.vcore->tb_offset;
break;
+ case KVM_REG_PPC_ONLINE:
+ i = set_reg_val(id, *val);
+ if (i && !vcpu->arch.online)
+ atomic_inc(&vcpu->arch.vcore->online_count);
+ else if (!i && vcpu->arch.online)
+ atomic_dec(&vcpu->arch.vcore->online_count);
+ vcpu->arch.online = i;
+ break;
default:
r = -EINVAL;
break;
}
}
+ /*
+ * On POWER8, set RWMR register.
+ * Since it only affects PURR and SPURR, it doesn't affect
+ * the host, so we don't save/restore the host value.
+ */
+ if (is_power8) {
+ unsigned long rwmr_val = RWMR_RPA_P8_8THREAD;
+ int n_online = atomic_read(&vc->online_count);
+
+ /*
+ * Use the 8-thread value if we're doing split-core
+ * or if the vcore's online count looks bogus.
+ */
+ if (split == 1 && threads_per_subcore == MAX_SMT_THREADS &&
+ n_online >= 1 && n_online <= MAX_SMT_THREADS)
+ rwmr_val = p8_rwmr_values[n_online];
+ mtspr(SPRN_RWMR, rwmr_val);
+ }
+
/* Start all the threads */
active = 0;
for (sub = 0; sub < core_info.n_subcores; ++sub) {
for (sub = 0; sub < core_info.n_subcores; ++sub)
spin_unlock(&core_info.vc[sub]->lock);
+ if (kvm_is_radix(vc->kvm)) {
+ int tmp = pcpu;
+
+ /*
+ * Do we need to flush the process scoped TLB for the LPAR?
+ *
+ * On POWER9, individual threads can come in here, but the
+ * TLB is shared between the 4 threads in a core, hence
+ * invalidating on one thread invalidates for all.
+ * Thus we make all 4 threads use the same bit here.
+ *
+ * Hash must be flushed in realmode in order to use tlbiel.
+ */
+ mtspr(SPRN_LPID, vc->kvm->arch.lpid);
+ isync();
+
+ if (cpu_has_feature(CPU_FTR_ARCH_300))
+ tmp &= ~0x3UL;
+
+ if (cpumask_test_cpu(tmp, &vc->kvm->arch.need_tlb_flush)) {
+ radix__local_flush_tlb_lpid_guest(vc->kvm->arch.lpid);
+ /* Clear the bit after the TLB flush */
+ cpumask_clear_cpu(tmp, &vc->kvm->arch.need_tlb_flush);
+ }
+ }
+
/*
* Interrupts will be enabled once we get into the guest,
* so tell lockdep that we're about to enable interrupts.
}
#endif
+ /*
+ * Force online to 1 for the sake of old userspace which doesn't
+ * set it.
+ */
+ if (!vcpu->arch.online) {
+ atomic_inc(&vcpu->arch.vcore->online_count);
+ vcpu->arch.online = 1;
+ }
+
kvmppc_core_prepare_to_enter(vcpu);
/* No need to go into the guest when all we'll do is come back out */
#include <linux/cma.h>
#include <linux/bitops.h>
+#include <asm/asm-prototypes.h>
#include <asm/cputable.h>
#include <asm/kvm_ppc.h>
#include <asm/kvm_book3s.h>
/* Only need to do the expensive mfmsr() on radix */
if (kvm_is_radix(vcpu->kvm) && (mfmsr() & MSR_IR))
- r = powernv_get_random_long(&vcpu->arch.gpr[4]);
+ r = powernv_get_random_long(&vcpu->arch.regs.gpr[4]);
else
- r = powernv_get_random_real_mode(&vcpu->arch.gpr[4]);
+ r = powernv_get_random_real_mode(&vcpu->arch.regs.gpr[4]);
if (r)
return H_SUCCESS;
{
if (!kvmppc_xics_enabled(vcpu))
return H_TOO_HARD;
- vcpu->arch.gpr[5] = get_tb();
+ vcpu->arch.regs.gpr[5] = get_tb();
if (xive_enabled()) {
if (is_rm())
return xive_rm_h_xirr(vcpu);
void kvmppc_bad_interrupt(struct pt_regs *regs)
{
- die("Bad interrupt in KVM entry/exit code", regs, SIGABRT);
+ /*
+ * 100 could happen at any time, 200 can happen due to invalid real
+ * address access for example (or any time due to a hardware problem).
+ */
+ if (TRAP(regs) == 0x100) {
+ get_paca()->in_nmi++;
+ system_reset_exception(regs);
+ get_paca()->in_nmi--;
+ } else if (TRAP(regs) == 0x200) {
+ machine_check_exception(regs);
+ } else {
+ die("Bad interrupt in KVM entry/exit code", regs, SIGABRT);
+ }
panic("Bad KVM trap");
}
/*
* We return here in virtual mode after the guest exits
* with something that we can't handle in real mode.
- * Interrupts are enabled again at this point.
+ * Interrupts are still hard-disabled.
*/
/*
long pte_index, unsigned long pteh, unsigned long ptel)
{
return kvmppc_do_h_enter(vcpu->kvm, flags, pte_index, pteh, ptel,
- vcpu->arch.pgdir, true, &vcpu->arch.gpr[4]);
+ vcpu->arch.pgdir, true,
+ &vcpu->arch.regs.gpr[4]);
}
#ifdef __BIG_ENDIAN__
(HPTE_R_KEY_HI | HPTE_R_KEY_LO));
}
-static inline int try_lock_tlbie(unsigned int *lock)
-{
- unsigned int tmp, old;
- unsigned int token = LOCK_TOKEN;
-
- asm volatile("1:lwarx %1,0,%2\n"
- " cmpwi cr0,%1,0\n"
- " bne 2f\n"
- " stwcx. %3,0,%2\n"
- " bne- 1b\n"
- " isync\n"
- "2:"
- : "=&r" (tmp), "=&r" (old)
- : "r" (lock), "r" (token)
- : "cc", "memory");
- return old == 0;
-}
-
static void do_tlbies(struct kvm *kvm, unsigned long *rbvalues,
long npages, int global, bool need_sync)
{
* the RS field, this is backwards-compatible with P7 and P8.
*/
if (global) {
- while (!try_lock_tlbie(&kvm->arch.tlbie_lock))
- cpu_relax();
if (need_sync)
asm volatile("ptesync" : : : "memory");
for (i = 0; i < npages; ++i) {
}
asm volatile("eieio; tlbsync; ptesync" : : : "memory");
- kvm->arch.tlbie_lock = 0;
} else {
if (need_sync)
asm volatile("ptesync" : : : "memory");
unsigned long pte_index, unsigned long avpn)
{
return kvmppc_do_h_remove(vcpu->kvm, flags, pte_index, avpn,
- &vcpu->arch.gpr[4]);
+ &vcpu->arch.regs.gpr[4]);
}
long kvmppc_h_bulk_remove(struct kvm_vcpu *vcpu)
{
struct kvm *kvm = vcpu->kvm;
- unsigned long *args = &vcpu->arch.gpr[4];
+ unsigned long *args = &vcpu->arch.regs.gpr[4];
__be64 *hp, *hptes[4];
unsigned long tlbrb[4];
long int i, j, k, n, found, indexes[4];
r = rev[i].guest_rpte | (r & (HPTE_R_R | HPTE_R_C));
r &= ~HPTE_GR_RESERVED;
}
- vcpu->arch.gpr[4 + i * 2] = v;
- vcpu->arch.gpr[5 + i * 2] = r;
+ vcpu->arch.regs.gpr[4 + i * 2] = v;
+ vcpu->arch.regs.gpr[5 + i * 2] = r;
}
return H_SUCCESS;
}
}
}
}
- vcpu->arch.gpr[4] = gr;
+ vcpu->arch.regs.gpr[4] = gr;
ret = H_SUCCESS;
out:
unlock_hpte(hpte, v & ~HPTE_V_HVLOCK);
kvmppc_set_dirty_from_hpte(kvm, v, gr);
}
}
- vcpu->arch.gpr[4] = gr;
+ vcpu->arch.regs.gpr[4] = gr;
ret = H_SUCCESS;
out:
unlock_hpte(hpte, v & ~HPTE_V_HVLOCK);
} while (!icp_rm_try_update(icp, old_state, new_state));
/* Return the result in GPR4 */
- vcpu->arch.gpr[4] = xirr;
+ vcpu->arch.regs.gpr[4] = xirr;
return check_too_hard(xics, icp);
}
extsw reg, reg; \
END_FTR_SECTION_IFCLR(CPU_FTR_ARCH_300)
-#define VCPU_GPRS_TM(reg) (((reg) * ULONG_SIZE) + VCPU_GPR_TM)
-
/* Values in HSTATE_NAPPING(r13) */
#define NAPPING_CEDE 1
#define NAPPING_NOVCPU 2
/* Primary thread switches to guest partition. */
cmpwi r6,0
bne 10f
+
+ /* Radix has already switched LPID and flushed core TLB */
+ bne cr7, 22f
+
lwz r7,KVM_LPID(r9)
BEGIN_FTR_SECTION
ld r6,KVM_SDR1(r9)
mtspr SPRN_LPID,r7
isync
- /* See if we need to flush the TLB */
+ /* See if we need to flush the TLB. Hash has to be done in RM */
lhz r6,PACAPACAINDEX(r13) /* test_bit(cpu, need_tlb_flush) */
BEGIN_FTR_SECTION
/*
li r7,0x800 /* IS field = 0b10 */
ptesync
li r0,0 /* RS for P9 version of tlbiel */
- bne cr7, 29f
28: tlbiel r7 /* On P9, rs=0, RIC=0, PRS=0, R=0 */
addi r7,r7,0x1000
bdnz 28b
- b 30f
-29: PPC_TLBIEL(7,0,2,1,1) /* for radix, RIC=2, PRS=1, R=1 */
- addi r7,r7,0x1000
- bdnz 29b
-30: ptesync
+ ptesync
23: ldarx r7,0,r6 /* clear the bit after TLB flushed */
andc r7,r7,r8
stdcx. r7,0,r6
/*
* NOTE THAT THIS TRASHES ALL NON-VOLATILE REGISTERS INCLUDING CR
*/
- bl kvmppc_restore_tm
+ mr r3, r4
+ ld r4, VCPU_MSR(r3)
+ bl kvmppc_restore_tm_hv
+ ld r4, HSTATE_KVM_VCPU(r13)
91:
#endif
/*
* NOTE THAT THIS TRASHES ALL NON-VOLATILE REGISTERS INCLUDING CR
*/
- bl kvmppc_save_tm
+ mr r3, r9
+ ld r4, VCPU_MSR(r3)
+ bl kvmppc_save_tm_hv
+ ld r9, HSTATE_KVM_VCPU(r13)
91:
#endif
/*
* NOTE THAT THIS TRASHES ALL NON-VOLATILE REGISTERS INCLUDING CR
*/
- ld r9, HSTATE_KVM_VCPU(r13)
- bl kvmppc_save_tm
+ ld r3, HSTATE_KVM_VCPU(r13)
+ ld r4, VCPU_MSR(r3)
+ bl kvmppc_save_tm_hv
91:
#endif
/*
* NOTE THAT THIS TRASHES ALL NON-VOLATILE REGISTERS INCLUDING CR
*/
- bl kvmppc_restore_tm
+ mr r3, r4
+ ld r4, VCPU_MSR(r3)
+ bl kvmppc_restore_tm_hv
+ ld r4, HSTATE_KVM_VCPU(r13)
91:
#endif
#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
/*
* Save transactional state and TM-related registers.
- * Called with r9 pointing to the vcpu struct.
+ * Called with r3 pointing to the vcpu struct and r4 containing
+ * the guest MSR value.
* This can modify all checkpointed registers, but
- * restores r1, r2 and r9 (vcpu pointer) before exit.
+ * restores r1 and r2 before exit.
*/
-kvmppc_save_tm:
+kvmppc_save_tm_hv:
+ /* See if we need to handle fake suspend mode */
+BEGIN_FTR_SECTION
+ b __kvmppc_save_tm
+END_FTR_SECTION_IFCLR(CPU_FTR_P9_TM_HV_ASSIST)
+
+ lbz r0, HSTATE_FAKE_SUSPEND(r13) /* Were we fake suspended? */
+ cmpwi r0, 0
+ beq __kvmppc_save_tm
+
+ /* The following code handles the fake_suspend = 1 case */
mflr r0
std r0, PPC_LR_STKOFF(r1)
stdu r1, -PPC_MIN_STKFRM(r1)
rldimi r8, r0, MSR_TM_LG, 63-MSR_TM_LG
mtmsrd r8
- ld r5, VCPU_MSR(r9)
- rldicl. r5, r5, 64 - MSR_TS_S_LG, 62
- beq 1f /* TM not active in guest. */
-
- std r1, HSTATE_HOST_R1(r13)
- li r3, TM_CAUSE_KVM_RESCHED
-
-BEGIN_FTR_SECTION
- lbz r0, HSTATE_FAKE_SUSPEND(r13) /* Were we fake suspended? */
- cmpwi r0, 0
- beq 3f
rldicl. r8, r8, 64 - MSR_TS_S_LG, 62 /* Did we actually hrfid? */
beq 4f
-BEGIN_FTR_SECTION_NESTED(96)
+BEGIN_FTR_SECTION
bl pnv_power9_force_smt4_catch
-END_FTR_SECTION_NESTED(CPU_FTR_P9_TM_XER_SO_BUG, CPU_FTR_P9_TM_XER_SO_BUG, 96)
+END_FTR_SECTION_IFSET(CPU_FTR_P9_TM_XER_SO_BUG)
nop
- b 6f
-3:
- /* Emulation of the treclaim instruction needs TEXASR before treclaim */
- mfspr r6, SPRN_TEXASR
- std r6, VCPU_ORIG_TEXASR(r9)
-6:
-END_FTR_SECTION_IFSET(CPU_FTR_P9_TM_HV_ASSIST)
- /* Clear the MSR RI since r1, r13 are all going to be foobar. */
+ std r1, HSTATE_HOST_R1(r13)
+
+ /* Clear the MSR RI since r1, r13 may be foobar. */
li r5, 0
mtmsrd r5, 1
- /* All GPRs are volatile at this point. */
+ /* We have to treclaim here because that's the only way to do S->N */
+ li r3, TM_CAUSE_KVM_RESCHED
TRECLAIM(R3)
- /* Temporarily store r13 and r9 so we have some regs to play with */
- SET_SCRATCH0(r13)
- GET_PACA(r13)
- std r9, PACATMSCRATCH(r13)
-
- /* If doing TM emulation on POWER9 DD2.2, check for fake suspend mode */
-BEGIN_FTR_SECTION
- lbz r9, HSTATE_FAKE_SUSPEND(r13)
- cmpwi r9, 0
- beq 2f
/*
* We were in fake suspend, so we are not going to save the
* register state as the guest checkpointed state (since
* we already have it), therefore we can now use any volatile GPR.
*/
- /* Reload stack pointer and TOC. */
+ /* Reload PACA pointer, stack pointer and TOC. */
+ GET_PACA(r13)
ld r1, HSTATE_HOST_R1(r13)
ld r2, PACATOC(r13)
+
/* Set MSR RI now we have r1 and r13 back. */
li r5, MSR_RI
mtmsrd r5, 1
+
HMT_MEDIUM
ld r6, HSTATE_DSCR(r13)
mtspr SPRN_DSCR, r6
li r0, PSSCR_FAKE_SUSPEND
andc r3, r3, r0
mtspr SPRN_PSSCR, r3
- ld r9, HSTATE_KVM_VCPU(r13)
- /* Don't save TEXASR, use value from last exit in real suspend state */
- b 11f
-2:
-END_FTR_SECTION_IFSET(CPU_FTR_P9_TM_HV_ASSIST)
+ /* Don't save TEXASR, use value from last exit in real suspend state */
ld r9, HSTATE_KVM_VCPU(r13)
-
- /* Get a few more GPRs free. */
- std r29, VCPU_GPRS_TM(29)(r9)
- std r30, VCPU_GPRS_TM(30)(r9)
- std r31, VCPU_GPRS_TM(31)(r9)
-
- /* Save away PPR and DSCR soon so don't run with user values. */
- mfspr r31, SPRN_PPR
- HMT_MEDIUM
- mfspr r30, SPRN_DSCR
- ld r29, HSTATE_DSCR(r13)
- mtspr SPRN_DSCR, r29
-
- /* Save all but r9, r13 & r29-r31 */
- reg = 0
- .rept 29
- .if (reg != 9) && (reg != 13)
- std reg, VCPU_GPRS_TM(reg)(r9)
- .endif
- reg = reg + 1
- .endr
- /* ... now save r13 */
- GET_SCRATCH0(r4)
- std r4, VCPU_GPRS_TM(13)(r9)
- /* ... and save r9 */
- ld r4, PACATMSCRATCH(r13)
- std r4, VCPU_GPRS_TM(9)(r9)
-
- /* Reload stack pointer and TOC. */
- ld r1, HSTATE_HOST_R1(r13)
- ld r2, PACATOC(r13)
-
- /* Set MSR RI now we have r1 and r13 back. */
- li r5, MSR_RI
- mtmsrd r5, 1
-
- /* Save away checkpinted SPRs. */
- std r31, VCPU_PPR_TM(r9)
- std r30, VCPU_DSCR_TM(r9)
- mflr r5
- mfcr r6
- mfctr r7
- mfspr r8, SPRN_AMR
- mfspr r10, SPRN_TAR
- mfxer r11
- std r5, VCPU_LR_TM(r9)
- stw r6, VCPU_CR_TM(r9)
- std r7, VCPU_CTR_TM(r9)
- std r8, VCPU_AMR_TM(r9)
- std r10, VCPU_TAR_TM(r9)
- std r11, VCPU_XER_TM(r9)
-
- /* Restore r12 as trap number. */
- lwz r12, VCPU_TRAP(r9)
-
- /* Save FP/VSX. */
- addi r3, r9, VCPU_FPRS_TM
- bl store_fp_state
- addi r3, r9, VCPU_VRS_TM
- bl store_vr_state
- mfspr r6, SPRN_VRSAVE
- stw r6, VCPU_VRSAVE_TM(r9)
-1:
- /*
- * We need to save these SPRs after the treclaim so that the software
- * error code is recorded correctly in the TEXASR. Also the user may
- * change these outside of a transaction, so they must always be
- * context switched.
- */
- mfspr r7, SPRN_TEXASR
- std r7, VCPU_TEXASR(r9)
-11:
mfspr r5, SPRN_TFHAR
mfspr r6, SPRN_TFIAR
std r5, VCPU_TFHAR(r9)
/*
* Restore transactional state and TM-related registers.
- * Called with r4 pointing to the vcpu struct.
+ * Called with r3 pointing to the vcpu struct
+ * and r4 containing the guest MSR value.
* This potentially modifies all checkpointed registers.
- * It restores r1, r2, r4 from the PACA.
+ * It restores r1 and r2 from the PACA.
*/
-kvmppc_restore_tm:
+kvmppc_restore_tm_hv:
+ /*
+ * If we are doing TM emulation for the guest on a POWER9 DD2,
+ * then we don't actually do a trechkpt -- we either set up
+ * fake-suspend mode, or emulate a TM rollback.
+ */
+BEGIN_FTR_SECTION
+ b __kvmppc_restore_tm
+END_FTR_SECTION_IFCLR(CPU_FTR_P9_TM_HV_ASSIST)
mflr r0
std r0, PPC_LR_STKOFF(r1)
- /* Turn on TM/FP/VSX/VMX so we can restore them. */
+ li r0, 0
+ stb r0, HSTATE_FAKE_SUSPEND(r13)
+
+ /* Turn on TM so we can restore TM SPRs */
mfmsr r5
- li r6, MSR_TM >> 32
- sldi r6, r6, 32
- or r5, r5, r6
- ori r5, r5, MSR_FP
- oris r5, r5, (MSR_VEC | MSR_VSX)@h
+ li r0, 1
+ rldimi r5, r0, MSR_TM_LG, 63-MSR_TM_LG
mtmsrd r5
/*
* The user may change these outside of a transaction, so they must
* always be context switched.
*/
- ld r5, VCPU_TFHAR(r4)
- ld r6, VCPU_TFIAR(r4)
- ld r7, VCPU_TEXASR(r4)
+ ld r5, VCPU_TFHAR(r3)
+ ld r6, VCPU_TFIAR(r3)
+ ld r7, VCPU_TEXASR(r3)
mtspr SPRN_TFHAR, r5
mtspr SPRN_TFIAR, r6
mtspr SPRN_TEXASR, r7
- li r0, 0
- stb r0, HSTATE_FAKE_SUSPEND(r13)
- ld r5, VCPU_MSR(r4)
- rldicl. r5, r5, 64 - MSR_TS_S_LG, 62
+ rldicl. r5, r4, 64 - MSR_TS_S_LG, 62
beqlr /* TM not active in guest */
- std r1, HSTATE_HOST_R1(r13)
- /* Make sure the failure summary is set, otherwise we'll program check
- * when we trechkpt. It's possible that this might have been not set
- * on a kvmppc_set_one_reg() call but we shouldn't let this crash the
- * host.
- */
+ /* Make sure the failure summary is set */
oris r7, r7, (TEXASR_FS)@h
mtspr SPRN_TEXASR, r7
- /*
- * If we are doing TM emulation for the guest on a POWER9 DD2,
- * then we don't actually do a trechkpt -- we either set up
- * fake-suspend mode, or emulate a TM rollback.
- */
-BEGIN_FTR_SECTION
- b .Ldo_tm_fake_load
-END_FTR_SECTION_IFSET(CPU_FTR_P9_TM_HV_ASSIST)
-
- /*
- * We need to load up the checkpointed state for the guest.
- * We need to do this early as it will blow away any GPRs, VSRs and
- * some SPRs.
- */
-
- mr r31, r4
- addi r3, r31, VCPU_FPRS_TM
- bl load_fp_state
- addi r3, r31, VCPU_VRS_TM
- bl load_vr_state
- mr r4, r31
- lwz r7, VCPU_VRSAVE_TM(r4)
- mtspr SPRN_VRSAVE, r7
-
- ld r5, VCPU_LR_TM(r4)
- lwz r6, VCPU_CR_TM(r4)
- ld r7, VCPU_CTR_TM(r4)
- ld r8, VCPU_AMR_TM(r4)
- ld r9, VCPU_TAR_TM(r4)
- ld r10, VCPU_XER_TM(r4)
- mtlr r5
- mtcr r6
- mtctr r7
- mtspr SPRN_AMR, r8
- mtspr SPRN_TAR, r9
- mtxer r10
-
- /*
- * Load up PPR and DSCR values but don't put them in the actual SPRs
- * till the last moment to avoid running with userspace PPR and DSCR for
- * too long.
- */
- ld r29, VCPU_DSCR_TM(r4)
- ld r30, VCPU_PPR_TM(r4)
-
- std r2, PACATMSCRATCH(r13) /* Save TOC */
-
- /* Clear the MSR RI since r1, r13 are all going to be foobar. */
- li r5, 0
- mtmsrd r5, 1
-
- /* Load GPRs r0-r28 */
- reg = 0
- .rept 29
- ld reg, VCPU_GPRS_TM(reg)(r31)
- reg = reg + 1
- .endr
-
- mtspr SPRN_DSCR, r29
- mtspr SPRN_PPR, r30
-
- /* Load final GPRs */
- ld 29, VCPU_GPRS_TM(29)(r31)
- ld 30, VCPU_GPRS_TM(30)(r31)
- ld 31, VCPU_GPRS_TM(31)(r31)
-
- /* TM checkpointed state is now setup. All GPRs are now volatile. */
- TRECHKPT
-
- /* Now let's get back the state we need. */
- HMT_MEDIUM
- GET_PACA(r13)
- ld r29, HSTATE_DSCR(r13)
- mtspr SPRN_DSCR, r29
- ld r4, HSTATE_KVM_VCPU(r13)
- ld r1, HSTATE_HOST_R1(r13)
- ld r2, PACATMSCRATCH(r13)
-
- /* Set the MSR RI since we have our registers back. */
- li r5, MSR_RI
- mtmsrd r5, 1
-9:
- ld r0, PPC_LR_STKOFF(r1)
- mtlr r0
- blr
-
-.Ldo_tm_fake_load:
cmpwi r5, 1 /* check for suspended state */
bgt 10f
stb r5, HSTATE_FAKE_SUSPEND(r13)
- b 9b /* and return */
+ b 9f /* and return */
10: stdu r1, -PPC_MIN_STKFRM(r1)
/* guest is in transactional state, so simulate rollback */
- mr r3, r4
bl kvmhv_emulate_tm_rollback
nop
- ld r4, HSTATE_KVM_VCPU(r13) /* our vcpu pointer has been trashed */
addi r1, r1, PPC_MIN_STKFRM
- b 9b
-#endif
+9: ld r0, PPC_LR_STKOFF(r1)
+ mtlr r0
+ blr
+#endif /* CONFIG_PPC_TRANSACTIONAL_MEM */
/*
* We come here if we get any exception or interrupt while we are
bcl 20, 31, .+4
5: mflr r3
addi r3, r3, 9f - 5b
+ li r4, -1
+ rldimi r3, r4, 62, 0 /* ensure 0xc000000000000000 bits are set */
ld r4, PACAKMSR(r13)
mtspr SPRN_SRR0, r3
mtspr SPRN_SRR1, r4
u64 texasr, tfiar;
u64 msr = vcpu->arch.shregs.msr;
- tfiar = vcpu->arch.pc & ~0x3ull;
+ tfiar = vcpu->arch.regs.nip & ~0x3ull;
texasr = (failure_cause << 56) | TEXASR_ABORT | TEXASR_FS | TEXASR_EXACT;
if (MSR_TM_SUSPENDED(vcpu->arch.shregs.msr))
texasr |= TEXASR_SUSP;
(newmsr & MSR_TM)));
newmsr = sanitize_msr(newmsr);
vcpu->arch.shregs.msr = newmsr;
- vcpu->arch.cfar = vcpu->arch.pc - 4;
- vcpu->arch.pc = vcpu->arch.shregs.srr0;
+ vcpu->arch.cfar = vcpu->arch.regs.nip - 4;
+ vcpu->arch.regs.nip = vcpu->arch.shregs.srr0;
return RESUME_GUEST;
case PPC_INST_RFEBB:
vcpu->arch.bescr = bescr;
msr = (msr & ~MSR_TS_MASK) | MSR_TS_T;
vcpu->arch.shregs.msr = msr;
- vcpu->arch.cfar = vcpu->arch.pc - 4;
- vcpu->arch.pc = vcpu->arch.ebbrr;
+ vcpu->arch.cfar = vcpu->arch.regs.nip - 4;
+ vcpu->arch.regs.nip = vcpu->arch.ebbrr;
return RESUME_GUEST;
case PPC_INST_MTMSRD:
return 0;
newmsr = sanitize_msr(newmsr);
vcpu->arch.shregs.msr = newmsr;
- vcpu->arch.cfar = vcpu->arch.pc - 4;
- vcpu->arch.pc = vcpu->arch.shregs.srr0;
+ vcpu->arch.cfar = vcpu->arch.regs.nip - 4;
+ vcpu->arch.regs.nip = vcpu->arch.shregs.srr0;
return 1;
case PPC_INST_RFEBB:
mtspr(SPRN_BESCR, bescr);
msr = (msr & ~MSR_TS_MASK) | MSR_TS_T;
vcpu->arch.shregs.msr = msr;
- vcpu->arch.cfar = vcpu->arch.pc - 4;
- vcpu->arch.pc = mfspr(SPRN_EBBRR);
+ vcpu->arch.cfar = vcpu->arch.regs.nip - 4;
+ vcpu->arch.regs.nip = mfspr(SPRN_EBBRR);
return 1;
case PPC_INST_MTMSRD:
void kvmhv_emulate_tm_rollback(struct kvm_vcpu *vcpu)
{
vcpu->arch.shregs.msr &= ~MSR_TS_MASK; /* go to N state */
- vcpu->arch.pc = vcpu->arch.tfhar;
+ vcpu->arch.regs.nip = vcpu->arch.tfhar;
copy_from_checkpoint(vcpu);
vcpu->arch.cr = (vcpu->arch.cr & 0x0fffffff) | 0xa0000000;
}
#include <linux/highmem.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
+#include <asm/asm-prototypes.h>
+#include <asm/tm.h>
#include "book3s.h"
static int kvmppc_handle_ext(struct kvm_vcpu *vcpu, unsigned int exit_nr,
ulong msr);
-static void kvmppc_giveup_fac(struct kvm_vcpu *vcpu, ulong fac);
+#ifdef CONFIG_PPC_BOOK3S_64
+static int kvmppc_handle_fac(struct kvm_vcpu *vcpu, ulong fac);
+#endif
/* Some compatibility defines */
#ifdef CONFIG_PPC_BOOK3S_32
if (kvmppc_is_split_real(vcpu))
kvmppc_fixup_split_real(vcpu);
+
+ kvmppc_restore_tm_pr(vcpu);
}
static void kvmppc_core_vcpu_put_pr(struct kvm_vcpu *vcpu)
kvmppc_giveup_ext(vcpu, MSR_FP | MSR_VEC | MSR_VSX);
kvmppc_giveup_fac(vcpu, FSCR_TAR_LG);
+ kvmppc_save_tm_pr(vcpu);
/* Enable AIL if supported */
if (cpu_has_feature(CPU_FTR_HVMODE) &&
{
struct kvmppc_book3s_shadow_vcpu *svcpu = svcpu_get(vcpu);
- svcpu->gpr[0] = vcpu->arch.gpr[0];
- svcpu->gpr[1] = vcpu->arch.gpr[1];
- svcpu->gpr[2] = vcpu->arch.gpr[2];
- svcpu->gpr[3] = vcpu->arch.gpr[3];
- svcpu->gpr[4] = vcpu->arch.gpr[4];
- svcpu->gpr[5] = vcpu->arch.gpr[5];
- svcpu->gpr[6] = vcpu->arch.gpr[6];
- svcpu->gpr[7] = vcpu->arch.gpr[7];
- svcpu->gpr[8] = vcpu->arch.gpr[8];
- svcpu->gpr[9] = vcpu->arch.gpr[9];
- svcpu->gpr[10] = vcpu->arch.gpr[10];
- svcpu->gpr[11] = vcpu->arch.gpr[11];
- svcpu->gpr[12] = vcpu->arch.gpr[12];
- svcpu->gpr[13] = vcpu->arch.gpr[13];
+ svcpu->gpr[0] = vcpu->arch.regs.gpr[0];
+ svcpu->gpr[1] = vcpu->arch.regs.gpr[1];
+ svcpu->gpr[2] = vcpu->arch.regs.gpr[2];
+ svcpu->gpr[3] = vcpu->arch.regs.gpr[3];
+ svcpu->gpr[4] = vcpu->arch.regs.gpr[4];
+ svcpu->gpr[5] = vcpu->arch.regs.gpr[5];
+ svcpu->gpr[6] = vcpu->arch.regs.gpr[6];
+ svcpu->gpr[7] = vcpu->arch.regs.gpr[7];
+ svcpu->gpr[8] = vcpu->arch.regs.gpr[8];
+ svcpu->gpr[9] = vcpu->arch.regs.gpr[9];
+ svcpu->gpr[10] = vcpu->arch.regs.gpr[10];
+ svcpu->gpr[11] = vcpu->arch.regs.gpr[11];
+ svcpu->gpr[12] = vcpu->arch.regs.gpr[12];
+ svcpu->gpr[13] = vcpu->arch.regs.gpr[13];
svcpu->cr = vcpu->arch.cr;
- svcpu->xer = vcpu->arch.xer;
- svcpu->ctr = vcpu->arch.ctr;
- svcpu->lr = vcpu->arch.lr;
- svcpu->pc = vcpu->arch.pc;
+ svcpu->xer = vcpu->arch.regs.xer;
+ svcpu->ctr = vcpu->arch.regs.ctr;
+ svcpu->lr = vcpu->arch.regs.link;
+ svcpu->pc = vcpu->arch.regs.nip;
#ifdef CONFIG_PPC_BOOK3S_64
svcpu->shadow_fscr = vcpu->arch.shadow_fscr;
#endif
svcpu_put(svcpu);
}
+static void kvmppc_recalc_shadow_msr(struct kvm_vcpu *vcpu)
+{
+ ulong guest_msr = kvmppc_get_msr(vcpu);
+ ulong smsr = guest_msr;
+
+ /* Guest MSR values */
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+ smsr &= MSR_FE0 | MSR_FE1 | MSR_SF | MSR_SE | MSR_BE | MSR_LE |
+ MSR_TM | MSR_TS_MASK;
+#else
+ smsr &= MSR_FE0 | MSR_FE1 | MSR_SF | MSR_SE | MSR_BE | MSR_LE;
+#endif
+ /* Process MSR values */
+ smsr |= MSR_ME | MSR_RI | MSR_IR | MSR_DR | MSR_PR | MSR_EE;
+ /* External providers the guest reserved */
+ smsr |= (guest_msr & vcpu->arch.guest_owned_ext);
+ /* 64-bit Process MSR values */
+#ifdef CONFIG_PPC_BOOK3S_64
+ smsr |= MSR_ISF | MSR_HV;
+#endif
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+ /*
+ * in guest privileged state, we want to fail all TM transactions.
+ * So disable MSR TM bit so that all tbegin. will be able to be
+ * trapped into host.
+ */
+ if (!(guest_msr & MSR_PR))
+ smsr &= ~MSR_TM;
+#endif
+ vcpu->arch.shadow_msr = smsr;
+}
+
/* Copy data touched by real-mode code from shadow vcpu back to vcpu */
void kvmppc_copy_from_svcpu(struct kvm_vcpu *vcpu)
{
struct kvmppc_book3s_shadow_vcpu *svcpu = svcpu_get(vcpu);
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+ ulong old_msr;
+#endif
/*
* Maybe we were already preempted and synced the svcpu from
if (!svcpu->in_use)
goto out;
- vcpu->arch.gpr[0] = svcpu->gpr[0];
- vcpu->arch.gpr[1] = svcpu->gpr[1];
- vcpu->arch.gpr[2] = svcpu->gpr[2];
- vcpu->arch.gpr[3] = svcpu->gpr[3];
- vcpu->arch.gpr[4] = svcpu->gpr[4];
- vcpu->arch.gpr[5] = svcpu->gpr[5];
- vcpu->arch.gpr[6] = svcpu->gpr[6];
- vcpu->arch.gpr[7] = svcpu->gpr[7];
- vcpu->arch.gpr[8] = svcpu->gpr[8];
- vcpu->arch.gpr[9] = svcpu->gpr[9];
- vcpu->arch.gpr[10] = svcpu->gpr[10];
- vcpu->arch.gpr[11] = svcpu->gpr[11];
- vcpu->arch.gpr[12] = svcpu->gpr[12];
- vcpu->arch.gpr[13] = svcpu->gpr[13];
+ vcpu->arch.regs.gpr[0] = svcpu->gpr[0];
+ vcpu->arch.regs.gpr[1] = svcpu->gpr[1];
+ vcpu->arch.regs.gpr[2] = svcpu->gpr[2];
+ vcpu->arch.regs.gpr[3] = svcpu->gpr[3];
+ vcpu->arch.regs.gpr[4] = svcpu->gpr[4];
+ vcpu->arch.regs.gpr[5] = svcpu->gpr[5];
+ vcpu->arch.regs.gpr[6] = svcpu->gpr[6];
+ vcpu->arch.regs.gpr[7] = svcpu->gpr[7];
+ vcpu->arch.regs.gpr[8] = svcpu->gpr[8];
+ vcpu->arch.regs.gpr[9] = svcpu->gpr[9];
+ vcpu->arch.regs.gpr[10] = svcpu->gpr[10];
+ vcpu->arch.regs.gpr[11] = svcpu->gpr[11];
+ vcpu->arch.regs.gpr[12] = svcpu->gpr[12];
+ vcpu->arch.regs.gpr[13] = svcpu->gpr[13];
vcpu->arch.cr = svcpu->cr;
- vcpu->arch.xer = svcpu->xer;
- vcpu->arch.ctr = svcpu->ctr;
- vcpu->arch.lr = svcpu->lr;
- vcpu->arch.pc = svcpu->pc;
+ vcpu->arch.regs.xer = svcpu->xer;
+ vcpu->arch.regs.ctr = svcpu->ctr;
+ vcpu->arch.regs.link = svcpu->lr;
+ vcpu->arch.regs.nip = svcpu->pc;
vcpu->arch.shadow_srr1 = svcpu->shadow_srr1;
vcpu->arch.fault_dar = svcpu->fault_dar;
vcpu->arch.fault_dsisr = svcpu->fault_dsisr;
to_book3s(vcpu)->vtb += get_vtb() - vcpu->arch.entry_vtb;
if (cpu_has_feature(CPU_FTR_ARCH_207S))
vcpu->arch.ic += mfspr(SPRN_IC) - vcpu->arch.entry_ic;
+
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+ /*
+ * Unlike other MSR bits, MSR[TS]bits can be changed at guest without
+ * notifying host:
+ * modified by unprivileged instructions like "tbegin"/"tend"/
+ * "tresume"/"tsuspend" in PR KVM guest.
+ *
+ * It is necessary to sync here to calculate a correct shadow_msr.
+ *
+ * privileged guest's tbegin will be failed at present. So we
+ * only take care of problem state guest.
+ */
+ old_msr = kvmppc_get_msr(vcpu);
+ if (unlikely((old_msr & MSR_PR) &&
+ (vcpu->arch.shadow_srr1 & (MSR_TS_MASK)) !=
+ (old_msr & (MSR_TS_MASK)))) {
+ old_msr &= ~(MSR_TS_MASK);
+ old_msr |= (vcpu->arch.shadow_srr1 & (MSR_TS_MASK));
+ kvmppc_set_msr_fast(vcpu, old_msr);
+ kvmppc_recalc_shadow_msr(vcpu);
+ }
+#endif
+
svcpu->in_use = false;
out:
svcpu_put(svcpu);
}
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+void kvmppc_save_tm_sprs(struct kvm_vcpu *vcpu)
+{
+ tm_enable();
+ vcpu->arch.tfhar = mfspr(SPRN_TFHAR);
+ vcpu->arch.texasr = mfspr(SPRN_TEXASR);
+ vcpu->arch.tfiar = mfspr(SPRN_TFIAR);
+ tm_disable();
+}
+
+void kvmppc_restore_tm_sprs(struct kvm_vcpu *vcpu)
+{
+ tm_enable();
+ mtspr(SPRN_TFHAR, vcpu->arch.tfhar);
+ mtspr(SPRN_TEXASR, vcpu->arch.texasr);
+ mtspr(SPRN_TFIAR, vcpu->arch.tfiar);
+ tm_disable();
+}
+
+/* loadup math bits which is enabled at kvmppc_get_msr() but not enabled at
+ * hardware.
+ */
+static void kvmppc_handle_lost_math_exts(struct kvm_vcpu *vcpu)
+{
+ ulong exit_nr;
+ ulong ext_diff = (kvmppc_get_msr(vcpu) & ~vcpu->arch.guest_owned_ext) &
+ (MSR_FP | MSR_VEC | MSR_VSX);
+
+ if (!ext_diff)
+ return;
+
+ if (ext_diff == MSR_FP)
+ exit_nr = BOOK3S_INTERRUPT_FP_UNAVAIL;
+ else if (ext_diff == MSR_VEC)
+ exit_nr = BOOK3S_INTERRUPT_ALTIVEC;
+ else
+ exit_nr = BOOK3S_INTERRUPT_VSX;
+
+ kvmppc_handle_ext(vcpu, exit_nr, ext_diff);
+}
+
+void kvmppc_save_tm_pr(struct kvm_vcpu *vcpu)
+{
+ if (!(MSR_TM_ACTIVE(kvmppc_get_msr(vcpu)))) {
+ kvmppc_save_tm_sprs(vcpu);
+ return;
+ }
+
+ kvmppc_giveup_fac(vcpu, FSCR_TAR_LG);
+ kvmppc_giveup_ext(vcpu, MSR_VSX);
+
+ preempt_disable();
+ _kvmppc_save_tm_pr(vcpu, mfmsr());
+ preempt_enable();
+}
+
+void kvmppc_restore_tm_pr(struct kvm_vcpu *vcpu)
+{
+ if (!MSR_TM_ACTIVE(kvmppc_get_msr(vcpu))) {
+ kvmppc_restore_tm_sprs(vcpu);
+ if (kvmppc_get_msr(vcpu) & MSR_TM) {
+ kvmppc_handle_lost_math_exts(vcpu);
+ if (vcpu->arch.fscr & FSCR_TAR)
+ kvmppc_handle_fac(vcpu, FSCR_TAR_LG);
+ }
+ return;
+ }
+
+ preempt_disable();
+ _kvmppc_restore_tm_pr(vcpu, kvmppc_get_msr(vcpu));
+ preempt_enable();
+
+ if (kvmppc_get_msr(vcpu) & MSR_TM) {
+ kvmppc_handle_lost_math_exts(vcpu);
+ if (vcpu->arch.fscr & FSCR_TAR)
+ kvmppc_handle_fac(vcpu, FSCR_TAR_LG);
+ }
+}
+#endif
+
static int kvmppc_core_check_requests_pr(struct kvm_vcpu *vcpu)
{
int r = 1; /* Indicate we want to get back into the guest */
/*****************************************/
-static void kvmppc_recalc_shadow_msr(struct kvm_vcpu *vcpu)
-{
- ulong guest_msr = kvmppc_get_msr(vcpu);
- ulong smsr = guest_msr;
-
- /* Guest MSR values */
- smsr &= MSR_FE0 | MSR_FE1 | MSR_SF | MSR_SE | MSR_BE | MSR_LE;
- /* Process MSR values */
- smsr |= MSR_ME | MSR_RI | MSR_IR | MSR_DR | MSR_PR | MSR_EE;
- /* External providers the guest reserved */
- smsr |= (guest_msr & vcpu->arch.guest_owned_ext);
- /* 64-bit Process MSR values */
-#ifdef CONFIG_PPC_BOOK3S_64
- smsr |= MSR_ISF | MSR_HV;
-#endif
- vcpu->arch.shadow_msr = smsr;
-}
-
static void kvmppc_set_msr_pr(struct kvm_vcpu *vcpu, u64 msr)
{
- ulong old_msr = kvmppc_get_msr(vcpu);
+ ulong old_msr;
+
+ /* For PAPR guest, make sure MSR reflects guest mode */
+ if (vcpu->arch.papr_enabled)
+ msr = (msr & ~MSR_HV) | MSR_ME;
#ifdef EXIT_DEBUG
printk(KERN_INFO "KVM: Set MSR to 0x%llx\n", msr);
#endif
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+ /* We should never target guest MSR to TS=10 && PR=0,
+ * since we always fail transaction for guest privilege
+ * state.
+ */
+ if (!(msr & MSR_PR) && MSR_TM_TRANSACTIONAL(msr))
+ kvmppc_emulate_tabort(vcpu,
+ TM_CAUSE_KVM_FAC_UNAV | TM_CAUSE_PERSISTENT);
+#endif
+
+ old_msr = kvmppc_get_msr(vcpu);
msr &= to_book3s(vcpu)->msr_mask;
kvmppc_set_msr_fast(vcpu, msr);
kvmppc_recalc_shadow_msr(vcpu);
/* Preload FPU if it's enabled */
if (kvmppc_get_msr(vcpu) & MSR_FP)
kvmppc_handle_ext(vcpu, BOOK3S_INTERRUPT_FP_UNAVAIL, MSR_FP);
+
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+ if (kvmppc_get_msr(vcpu) & MSR_TM)
+ kvmppc_handle_lost_math_exts(vcpu);
+#endif
}
void kvmppc_set_pvr_pr(struct kvm_vcpu *vcpu, u32 pvr)
pte.may_execute = !data;
}
- if (page_found == -ENOENT) {
- /* Page not found in guest PTE entries */
- u64 ssrr1 = vcpu->arch.shadow_srr1;
- u64 msr = kvmppc_get_msr(vcpu);
- kvmppc_set_dar(vcpu, kvmppc_get_fault_dar(vcpu));
- kvmppc_set_dsisr(vcpu, vcpu->arch.fault_dsisr);
- kvmppc_set_msr_fast(vcpu, msr | (ssrr1 & 0xf8000000ULL));
- kvmppc_book3s_queue_irqprio(vcpu, vec);
- } else if (page_found == -EPERM) {
- /* Storage protection */
- u32 dsisr = vcpu->arch.fault_dsisr;
- u64 ssrr1 = vcpu->arch.shadow_srr1;
- u64 msr = kvmppc_get_msr(vcpu);
- kvmppc_set_dar(vcpu, kvmppc_get_fault_dar(vcpu));
- dsisr = (dsisr & ~DSISR_NOHPTE) | DSISR_PROTFAULT;
- kvmppc_set_dsisr(vcpu, dsisr);
- kvmppc_set_msr_fast(vcpu, msr | (ssrr1 & 0xf8000000ULL));
- kvmppc_book3s_queue_irqprio(vcpu, vec);
+ if (page_found == -ENOENT || page_found == -EPERM) {
+ /* Page not found in guest PTE entries, or protection fault */
+ u64 flags;
+
+ if (page_found == -EPERM)
+ flags = DSISR_PROTFAULT;
+ else
+ flags = DSISR_NOHPTE;
+ if (data) {
+ flags |= vcpu->arch.fault_dsisr & DSISR_ISSTORE;
+ kvmppc_core_queue_data_storage(vcpu, eaddr, flags);
+ } else {
+ kvmppc_core_queue_inst_storage(vcpu, flags);
+ }
} else if (page_found == -EINVAL) {
/* Page not found in guest SLB */
kvmppc_set_dar(vcpu, kvmppc_get_fault_dar(vcpu));
}
/* Give up facility (TAR / EBB / DSCR) */
-static void kvmppc_giveup_fac(struct kvm_vcpu *vcpu, ulong fac)
+void kvmppc_giveup_fac(struct kvm_vcpu *vcpu, ulong fac)
{
#ifdef CONFIG_PPC_BOOK3S_64
if (!(vcpu->arch.shadow_fscr & (1ULL << fac))) {
#ifdef CONFIG_PPC_BOOK3S_64
-static void kvmppc_trigger_fac_interrupt(struct kvm_vcpu *vcpu, ulong fac)
+void kvmppc_trigger_fac_interrupt(struct kvm_vcpu *vcpu, ulong fac)
{
/* Inject the Interrupt Cause field and trigger a guest interrupt */
vcpu->arch.fscr &= ~(0xffULL << 56);
break;
}
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+ /* Since we disabled MSR_TM at privilege state, the mfspr instruction
+ * for TM spr can trigger TM fac unavailable. In this case, the
+ * emulation is handled by kvmppc_emulate_fac(), which invokes
+ * kvmppc_emulate_mfspr() finally. But note the mfspr can include
+ * RT for NV registers. So it need to restore those NV reg to reflect
+ * the update.
+ */
+ if ((fac == FSCR_TM_LG) && !(kvmppc_get_msr(vcpu) & MSR_PR))
+ return RESUME_GUEST_NV;
+#endif
+
return RESUME_GUEST;
}
if ((vcpu->arch.fscr & FSCR_TAR) && !(fscr & FSCR_TAR)) {
/* TAR got dropped, drop it in shadow too */
kvmppc_giveup_fac(vcpu, FSCR_TAR_LG);
+ } else if (!(vcpu->arch.fscr & FSCR_TAR) && (fscr & FSCR_TAR)) {
+ vcpu->arch.fscr = fscr;
+ kvmppc_handle_fac(vcpu, FSCR_TAR_LG);
+ return;
}
+
vcpu->arch.fscr = fscr;
}
#endif
kvmppc_mmu_pte_flush(vcpu, kvmppc_get_pc(vcpu), ~0xFFFUL);
r = RESUME_GUEST;
} else {
- u64 msr = kvmppc_get_msr(vcpu);
- msr |= shadow_srr1 & 0x58000000;
- kvmppc_set_msr_fast(vcpu, msr);
- kvmppc_book3s_queue_irqprio(vcpu, exit_nr);
+ kvmppc_core_queue_inst_storage(vcpu,
+ shadow_srr1 & 0x58000000);
r = RESUME_GUEST;
}
break;
r = kvmppc_handle_pagefault(run, vcpu, dar, exit_nr);
srcu_read_unlock(&vcpu->kvm->srcu, idx);
} else {
- kvmppc_set_dar(vcpu, dar);
- kvmppc_set_dsisr(vcpu, fault_dsisr);
- kvmppc_book3s_queue_irqprio(vcpu, exit_nr);
+ kvmppc_core_queue_data_storage(vcpu, dar, fault_dsisr);
r = RESUME_GUEST;
}
break;
case BOOK3S_INTERRUPT_EXTERNAL:
case BOOK3S_INTERRUPT_EXTERNAL_LEVEL:
case BOOK3S_INTERRUPT_EXTERNAL_HV:
+ case BOOK3S_INTERRUPT_H_VIRT:
vcpu->stat.ext_intr_exits++;
r = RESUME_GUEST;
break;
+ case BOOK3S_INTERRUPT_HMI:
case BOOK3S_INTERRUPT_PERFMON:
+ case BOOK3S_INTERRUPT_SYSTEM_RESET:
r = RESUME_GUEST;
break;
case BOOK3S_INTERRUPT_PROGRAM:
}
#ifdef CONFIG_PPC_BOOK3S_64
case BOOK3S_INTERRUPT_FAC_UNAVAIL:
- kvmppc_handle_fac(vcpu, vcpu->arch.shadow_fscr >> 56);
- r = RESUME_GUEST;
+ r = kvmppc_handle_fac(vcpu, vcpu->arch.shadow_fscr >> 56);
break;
#endif
case BOOK3S_INTERRUPT_MACHINE_CHECK:
else
*val = get_reg_val(id, 0);
break;
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+ case KVM_REG_PPC_TFHAR:
+ *val = get_reg_val(id, vcpu->arch.tfhar);
+ break;
+ case KVM_REG_PPC_TFIAR:
+ *val = get_reg_val(id, vcpu->arch.tfiar);
+ break;
+ case KVM_REG_PPC_TEXASR:
+ *val = get_reg_val(id, vcpu->arch.texasr);
+ break;
+ case KVM_REG_PPC_TM_GPR0 ... KVM_REG_PPC_TM_GPR31:
+ *val = get_reg_val(id,
+ vcpu->arch.gpr_tm[id-KVM_REG_PPC_TM_GPR0]);
+ break;
+ case KVM_REG_PPC_TM_VSR0 ... KVM_REG_PPC_TM_VSR63:
+ {
+ int i, j;
+
+ i = id - KVM_REG_PPC_TM_VSR0;
+ if (i < 32)
+ for (j = 0; j < TS_FPRWIDTH; j++)
+ val->vsxval[j] = vcpu->arch.fp_tm.fpr[i][j];
+ else {
+ if (cpu_has_feature(CPU_FTR_ALTIVEC))
+ val->vval = vcpu->arch.vr_tm.vr[i-32];
+ else
+ r = -ENXIO;
+ }
+ break;
+ }
+ case KVM_REG_PPC_TM_CR:
+ *val = get_reg_val(id, vcpu->arch.cr_tm);
+ break;
+ case KVM_REG_PPC_TM_XER:
+ *val = get_reg_val(id, vcpu->arch.xer_tm);
+ break;
+ case KVM_REG_PPC_TM_LR:
+ *val = get_reg_val(id, vcpu->arch.lr_tm);
+ break;
+ case KVM_REG_PPC_TM_CTR:
+ *val = get_reg_val(id, vcpu->arch.ctr_tm);
+ break;
+ case KVM_REG_PPC_TM_FPSCR:
+ *val = get_reg_val(id, vcpu->arch.fp_tm.fpscr);
+ break;
+ case KVM_REG_PPC_TM_AMR:
+ *val = get_reg_val(id, vcpu->arch.amr_tm);
+ break;
+ case KVM_REG_PPC_TM_PPR:
+ *val = get_reg_val(id, vcpu->arch.ppr_tm);
+ break;
+ case KVM_REG_PPC_TM_VRSAVE:
+ *val = get_reg_val(id, vcpu->arch.vrsave_tm);
+ break;
+ case KVM_REG_PPC_TM_VSCR:
+ if (cpu_has_feature(CPU_FTR_ALTIVEC))
+ *val = get_reg_val(id, vcpu->arch.vr_tm.vscr.u[3]);
+ else
+ r = -ENXIO;
+ break;
+ case KVM_REG_PPC_TM_DSCR:
+ *val = get_reg_val(id, vcpu->arch.dscr_tm);
+ break;
+ case KVM_REG_PPC_TM_TAR:
+ *val = get_reg_val(id, vcpu->arch.tar_tm);
+ break;
+#endif
default:
r = -EINVAL;
break;
case KVM_REG_PPC_LPCR_64:
kvmppc_set_lpcr_pr(vcpu, set_reg_val(id, *val));
break;
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+ case KVM_REG_PPC_TFHAR:
+ vcpu->arch.tfhar = set_reg_val(id, *val);
+ break;
+ case KVM_REG_PPC_TFIAR:
+ vcpu->arch.tfiar = set_reg_val(id, *val);
+ break;
+ case KVM_REG_PPC_TEXASR:
+ vcpu->arch.texasr = set_reg_val(id, *val);
+ break;
+ case KVM_REG_PPC_TM_GPR0 ... KVM_REG_PPC_TM_GPR31:
+ vcpu->arch.gpr_tm[id - KVM_REG_PPC_TM_GPR0] =
+ set_reg_val(id, *val);
+ break;
+ case KVM_REG_PPC_TM_VSR0 ... KVM_REG_PPC_TM_VSR63:
+ {
+ int i, j;
+
+ i = id - KVM_REG_PPC_TM_VSR0;
+ if (i < 32)
+ for (j = 0; j < TS_FPRWIDTH; j++)
+ vcpu->arch.fp_tm.fpr[i][j] = val->vsxval[j];
+ else
+ if (cpu_has_feature(CPU_FTR_ALTIVEC))
+ vcpu->arch.vr_tm.vr[i-32] = val->vval;
+ else
+ r = -ENXIO;
+ break;
+ }
+ case KVM_REG_PPC_TM_CR:
+ vcpu->arch.cr_tm = set_reg_val(id, *val);
+ break;
+ case KVM_REG_PPC_TM_XER:
+ vcpu->arch.xer_tm = set_reg_val(id, *val);
+ break;
+ case KVM_REG_PPC_TM_LR:
+ vcpu->arch.lr_tm = set_reg_val(id, *val);
+ break;
+ case KVM_REG_PPC_TM_CTR:
+ vcpu->arch.ctr_tm = set_reg_val(id, *val);
+ break;
+ case KVM_REG_PPC_TM_FPSCR:
+ vcpu->arch.fp_tm.fpscr = set_reg_val(id, *val);
+ break;
+ case KVM_REG_PPC_TM_AMR:
+ vcpu->arch.amr_tm = set_reg_val(id, *val);
+ break;
+ case KVM_REG_PPC_TM_PPR:
+ vcpu->arch.ppr_tm = set_reg_val(id, *val);
+ break;
+ case KVM_REG_PPC_TM_VRSAVE:
+ vcpu->arch.vrsave_tm = set_reg_val(id, *val);
+ break;
+ case KVM_REG_PPC_TM_VSCR:
+ if (cpu_has_feature(CPU_FTR_ALTIVEC))
+ vcpu->arch.vr.vscr.u[3] = set_reg_val(id, *val);
+ else
+ r = -ENXIO;
+ break;
+ case KVM_REG_PPC_TM_DSCR:
+ vcpu->arch.dscr_tm = set_reg_val(id, *val);
+ break;
+ case KVM_REG_PPC_TM_TAR:
+ vcpu->arch.tar_tm = set_reg_val(id, *val);
+ break;
+#endif
default:
r = -EINVAL;
break;
return 0;
}
+
+static int kvm_configure_mmu_pr(struct kvm *kvm, struct kvm_ppc_mmuv3_cfg *cfg)
+{
+ if (!cpu_has_feature(CPU_FTR_ARCH_300))
+ return -ENODEV;
+ /* Require flags and process table base and size to all be zero. */
+ if (cfg->flags || cfg->process_table)
+ return -EINVAL;
+ return 0;
+}
+
#else
static int kvm_vm_ioctl_get_smmu_info_pr(struct kvm *kvm,
struct kvm_ppc_smmu_info *info)
static int kvmppc_core_check_processor_compat_pr(void)
{
/*
- * Disable KVM for Power9 untill the required bits merged.
+ * PR KVM can work on POWER9 inside a guest partition
+ * running in HPT mode. It can't work if we are using
+ * radix translation (because radix provides no way for
+ * a process to have unique translations in quadrant 3).
*/
- if (cpu_has_feature(CPU_FTR_ARCH_300))
+ if (cpu_has_feature(CPU_FTR_ARCH_300) && radix_enabled())
return -EIO;
return 0;
}
.arch_vm_ioctl = kvm_arch_vm_ioctl_pr,
#ifdef CONFIG_PPC_BOOK3S_64
.hcall_implemented = kvmppc_hcall_impl_pr,
+ .configure_mmu = kvm_configure_mmu_pr,
#endif
+ .giveup_ext = kvmppc_giveup_ext,
};
*/
PPC_LL r6, HSTATE_HOST_MSR(r13)
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+ /*
+ * We don't want to change MSR[TS] bits via rfi here.
+ * The actual TM handling logic will be in host with
+ * recovered DR/IR bits after HSTATE_VMHANDLER.
+ * And MSR_TM can be enabled in HOST_MSR so rfid may
+ * not suppress this change and can lead to exception.
+ * Manually set MSR to prevent TS state change here.
+ */
+ mfmsr r7
+ rldicl r7, r7, 64 - MSR_TS_S_LG, 62
+ rldimi r6, r7, MSR_TS_S_LG, 63 - MSR_TS_T_LG
+#endif
PPC_LL r8, HSTATE_VMHANDLER(r13)
#ifdef CONFIG_PPC64
*/
/* Return interrupt and old CPPR in GPR4 */
- vcpu->arch.gpr[4] = hirq | (old_cppr << 24);
+ vcpu->arch.regs.gpr[4] = hirq | (old_cppr << 24);
return H_SUCCESS;
}
hirq = GLUE(X_PFX,scan_interrupts)(xc, pending, scan_poll);
/* Return interrupt and old CPPR in GPR4 */
- vcpu->arch.gpr[4] = hirq | (xc->cppr << 24);
+ vcpu->arch.regs.gpr[4] = hirq | (xc->cppr << 24);
return H_SUCCESS;
}
{
int i;
- printk("pc: %08lx msr: %08llx\n", vcpu->arch.pc, vcpu->arch.shared->msr);
- printk("lr: %08lx ctr: %08lx\n", vcpu->arch.lr, vcpu->arch.ctr);
+ printk("pc: %08lx msr: %08llx\n", vcpu->arch.regs.nip,
+ vcpu->arch.shared->msr);
+ printk("lr: %08lx ctr: %08lx\n", vcpu->arch.regs.link,
+ vcpu->arch.regs.ctr);
printk("srr0: %08llx srr1: %08llx\n", vcpu->arch.shared->srr0,
vcpu->arch.shared->srr1);
if (allowed) {
switch (int_class) {
case INT_CLASS_NONCRIT:
- set_guest_srr(vcpu, vcpu->arch.pc,
+ set_guest_srr(vcpu, vcpu->arch.regs.nip,
vcpu->arch.shared->msr);
break;
case INT_CLASS_CRIT:
- set_guest_csrr(vcpu, vcpu->arch.pc,
+ set_guest_csrr(vcpu, vcpu->arch.regs.nip,
vcpu->arch.shared->msr);
break;
case INT_CLASS_DBG:
- set_guest_dsrr(vcpu, vcpu->arch.pc,
+ set_guest_dsrr(vcpu, vcpu->arch.regs.nip,
vcpu->arch.shared->msr);
break;
case INT_CLASS_MC:
- set_guest_mcsrr(vcpu, vcpu->arch.pc,
+ set_guest_mcsrr(vcpu, vcpu->arch.regs.nip,
vcpu->arch.shared->msr);
break;
}
- vcpu->arch.pc = vcpu->arch.ivpr | vcpu->arch.ivor[priority];
+ vcpu->arch.regs.nip = vcpu->arch.ivpr |
+ vcpu->arch.ivor[priority];
if (update_esr == true)
kvmppc_set_esr(vcpu, vcpu->arch.queued_esr);
if (update_dear == true)
case EMULATE_FAIL:
printk(KERN_CRIT "%s: emulation at %lx failed (%08x)\n",
- __func__, vcpu->arch.pc, vcpu->arch.last_inst);
+ __func__, vcpu->arch.regs.nip, vcpu->arch.last_inst);
/* For debugging, encode the failing instruction and
* report it to userspace. */
run->hw.hardware_exit_reason = ~0ULL << 32;
*/
vcpu->arch.dbsr = 0;
run->debug.arch.status = 0;
- run->debug.arch.address = vcpu->arch.pc;
+ run->debug.arch.address = vcpu->arch.regs.nip;
if (dbsr & (DBSR_IAC1 | DBSR_IAC2 | DBSR_IAC3 | DBSR_IAC4)) {
run->debug.arch.status |= KVMPPC_DEBUG_BREAKPOINT;
case EMULATE_FAIL:
pr_debug("%s: load instruction from guest address %lx failed\n",
- __func__, vcpu->arch.pc);
+ __func__, vcpu->arch.regs.nip);
/* For debugging, encode the failing instruction and
* report it to userspace. */
run->hw.hardware_exit_reason = ~0ULL << 32;
case BOOKE_INTERRUPT_SPE_FP_DATA:
case BOOKE_INTERRUPT_SPE_FP_ROUND:
printk(KERN_CRIT "%s: unexpected SPE interrupt %u at %08lx\n",
- __func__, exit_nr, vcpu->arch.pc);
+ __func__, exit_nr, vcpu->arch.regs.nip);
run->hw.hardware_exit_reason = exit_nr;
r = RESUME_HOST;
break;
}
case BOOKE_INTERRUPT_ITLB_MISS: {
- unsigned long eaddr = vcpu->arch.pc;
+ unsigned long eaddr = vcpu->arch.regs.nip;
gpa_t gpaddr;
gfn_t gfn;
int gtlb_index;
int i;
int r;
- vcpu->arch.pc = 0;
+ vcpu->arch.regs.nip = 0;
vcpu->arch.shared->pir = vcpu->vcpu_id;
kvmppc_set_gpr(vcpu, 1, (16<<20) - 8); /* -8 for the callee-save LR slot */
kvmppc_set_msr(vcpu, 0);
vcpu_load(vcpu);
- regs->pc = vcpu->arch.pc;
+ regs->pc = vcpu->arch.regs.nip;
regs->cr = kvmppc_get_cr(vcpu);
- regs->ctr = vcpu->arch.ctr;
- regs->lr = vcpu->arch.lr;
+ regs->ctr = vcpu->arch.regs.ctr;
+ regs->lr = vcpu->arch.regs.link;
regs->xer = kvmppc_get_xer(vcpu);
regs->msr = vcpu->arch.shared->msr;
regs->srr0 = kvmppc_get_srr0(vcpu);
vcpu_load(vcpu);
- vcpu->arch.pc = regs->pc;
+ vcpu->arch.regs.nip = regs->pc;
kvmppc_set_cr(vcpu, regs->cr);
- vcpu->arch.ctr = regs->ctr;
- vcpu->arch.lr = regs->lr;
+ vcpu->arch.regs.ctr = regs->ctr;
+ vcpu->arch.regs.link = regs->lr;
kvmppc_set_xer(vcpu, regs->xer);
kvmppc_set_msr(vcpu, regs->msr);
kvmppc_set_srr0(vcpu, regs->srr0);
static void kvmppc_emul_rfi(struct kvm_vcpu *vcpu)
{
- vcpu->arch.pc = vcpu->arch.shared->srr0;
+ vcpu->arch.regs.nip = vcpu->arch.shared->srr0;
kvmppc_set_msr(vcpu, vcpu->arch.shared->srr1);
}
static void kvmppc_emul_rfdi(struct kvm_vcpu *vcpu)
{
- vcpu->arch.pc = vcpu->arch.dsrr0;
+ vcpu->arch.regs.nip = vcpu->arch.dsrr0;
kvmppc_set_msr(vcpu, vcpu->arch.dsrr1);
}
static void kvmppc_emul_rfci(struct kvm_vcpu *vcpu)
{
- vcpu->arch.pc = vcpu->arch.csrr0;
+ vcpu->arch.regs.nip = vcpu->arch.csrr0;
kvmppc_set_msr(vcpu, vcpu->arch.csrr1);
}
static int kvmppc_e500_emul_msgclr(struct kvm_vcpu *vcpu, int rb)
{
- ulong param = vcpu->arch.gpr[rb];
+ ulong param = vcpu->arch.regs.gpr[rb];
int prio = dbell2prio(param);
if (prio < 0)
static int kvmppc_e500_emul_msgsnd(struct kvm_vcpu *vcpu, int rb)
{
- ulong param = vcpu->arch.gpr[rb];
+ ulong param = vcpu->arch.regs.gpr[rb];
int prio = dbell2prio(rb);
int pir = param & PPC_DBELL_PIR_MASK;
int i;
switch (get_oc(inst)) {
case EHPRIV_OC_DEBUG:
run->exit_reason = KVM_EXIT_DEBUG;
- run->debug.arch.address = vcpu->arch.pc;
+ run->debug.arch.address = vcpu->arch.regs.nip;
run->debug.arch.status = 0;
kvmppc_account_exit(vcpu, DEBUG_EXITS);
emulated = EMULATE_EXIT_USER;
{
unsigned int as = !!(vcpu->arch.shared->msr & MSR_IS);
- kvmppc_e500_deliver_tlb_miss(vcpu, vcpu->arch.pc, as);
+ kvmppc_e500_deliver_tlb_miss(vcpu, vcpu->arch.regs.nip, as);
}
void kvmppc_mmu_dtlb_miss(struct kvm_vcpu *vcpu)
}
#ifdef CONFIG_KVM_BOOKE_HV
-int kvmppc_load_last_inst(struct kvm_vcpu *vcpu, enum instruction_type type,
- u32 *instr)
+int kvmppc_load_last_inst(struct kvm_vcpu *vcpu,
+ enum instruction_fetch_type type, u32 *instr)
{
gva_t geaddr;
hpa_t addr;
return EMULATE_DONE;
}
#else
-int kvmppc_load_last_inst(struct kvm_vcpu *vcpu, enum instruction_type type,
- u32 *instr)
+int kvmppc_load_last_inst(struct kvm_vcpu *vcpu,
+ enum instruction_fetch_type type, u32 *instr)
{
return EMULATE_AGAIN;
}
#include <asm/kvm_ppc.h>
#include <asm/disassemble.h>
#include <asm/ppc-opcode.h>
+#include <asm/sstep.h>
#include "timing.h"
#include "trace.h"
struct kvm_run *run = vcpu->run;
u32 inst;
int ra, rs, rt;
- enum emulation_result emulated;
+ enum emulation_result emulated = EMULATE_FAIL;
int advance = 1;
+ struct instruction_op op;
/* this default type might be overwritten by subcategories */
kvmppc_set_exit_type(vcpu, EMULATED_INST_EXITS);
vcpu->arch.mmio_vsx_tx_sx_enabled = get_tx_or_sx(inst);
vcpu->arch.mmio_vsx_copy_nums = 0;
vcpu->arch.mmio_vsx_offset = 0;
- vcpu->arch.mmio_vsx_copy_type = KVMPPC_VSX_COPY_NONE;
+ vcpu->arch.mmio_copy_type = KVMPPC_VSX_COPY_NONE;
vcpu->arch.mmio_sp64_extend = 0;
vcpu->arch.mmio_sign_extend = 0;
vcpu->arch.mmio_vmx_copy_nums = 0;
+ vcpu->arch.mmio_vmx_offset = 0;
+ vcpu->arch.mmio_host_swabbed = 0;
- switch (get_op(inst)) {
- case 31:
- switch (get_xop(inst)) {
- case OP_31_XOP_LWZX:
- emulated = kvmppc_handle_load(run, vcpu, rt, 4, 1);
- break;
-
- case OP_31_XOP_LWZUX:
- emulated = kvmppc_handle_load(run, vcpu, rt, 4, 1);
- kvmppc_set_gpr(vcpu, ra, vcpu->arch.vaddr_accessed);
- break;
+ emulated = EMULATE_FAIL;
+ vcpu->arch.regs.msr = vcpu->arch.shared->msr;
+ vcpu->arch.regs.ccr = vcpu->arch.cr;
+ if (analyse_instr(&op, &vcpu->arch.regs, inst) == 0) {
+ int type = op.type & INSTR_TYPE_MASK;
+ int size = GETSIZE(op.type);
- case OP_31_XOP_LBZX:
- emulated = kvmppc_handle_load(run, vcpu, rt, 1, 1);
- break;
-
- case OP_31_XOP_LBZUX:
- emulated = kvmppc_handle_load(run, vcpu, rt, 1, 1);
- kvmppc_set_gpr(vcpu, ra, vcpu->arch.vaddr_accessed);
- break;
+ switch (type) {
+ case LOAD: {
+ int instr_byte_swap = op.type & BYTEREV;
- case OP_31_XOP_STDX:
- emulated = kvmppc_handle_store(run, vcpu,
- kvmppc_get_gpr(vcpu, rs), 8, 1);
- break;
+ if (op.type & SIGNEXT)
+ emulated = kvmppc_handle_loads(run, vcpu,
+ op.reg, size, !instr_byte_swap);
+ else
+ emulated = kvmppc_handle_load(run, vcpu,
+ op.reg, size, !instr_byte_swap);
- case OP_31_XOP_STDUX:
- emulated = kvmppc_handle_store(run, vcpu,
- kvmppc_get_gpr(vcpu, rs), 8, 1);
- kvmppc_set_gpr(vcpu, ra, vcpu->arch.vaddr_accessed);
- break;
+ if ((op.type & UPDATE) && (emulated != EMULATE_FAIL))
+ kvmppc_set_gpr(vcpu, op.update_reg, op.ea);
- case OP_31_XOP_STWX:
- emulated = kvmppc_handle_store(run, vcpu,
- kvmppc_get_gpr(vcpu, rs), 4, 1);
- break;
-
- case OP_31_XOP_STWUX:
- emulated = kvmppc_handle_store(run, vcpu,
- kvmppc_get_gpr(vcpu, rs), 4, 1);
- kvmppc_set_gpr(vcpu, ra, vcpu->arch.vaddr_accessed);
- break;
-
- case OP_31_XOP_STBX:
- emulated = kvmppc_handle_store(run, vcpu,
- kvmppc_get_gpr(vcpu, rs), 1, 1);
- break;
-
- case OP_31_XOP_STBUX:
- emulated = kvmppc_handle_store(run, vcpu,
- kvmppc_get_gpr(vcpu, rs), 1, 1);
- kvmppc_set_gpr(vcpu, ra, vcpu->arch.vaddr_accessed);
- break;
-
- case OP_31_XOP_LHAX:
- emulated = kvmppc_handle_loads(run, vcpu, rt, 2, 1);
- break;
-
- case OP_31_XOP_LHAUX:
- emulated = kvmppc_handle_loads(run, vcpu, rt, 2, 1);
- kvmppc_set_gpr(vcpu, ra, vcpu->arch.vaddr_accessed);
- break;
-
- case OP_31_XOP_LHZX:
- emulated = kvmppc_handle_load(run, vcpu, rt, 2, 1);
- break;
-
- case OP_31_XOP_LHZUX:
- emulated = kvmppc_handle_load(run, vcpu, rt, 2, 1);
- kvmppc_set_gpr(vcpu, ra, vcpu->arch.vaddr_accessed);
- break;
-
- case OP_31_XOP_STHX:
- emulated = kvmppc_handle_store(run, vcpu,
- kvmppc_get_gpr(vcpu, rs), 2, 1);
- break;
-
- case OP_31_XOP_STHUX:
- emulated = kvmppc_handle_store(run, vcpu,
- kvmppc_get_gpr(vcpu, rs), 2, 1);
- kvmppc_set_gpr(vcpu, ra, vcpu->arch.vaddr_accessed);
- break;
-
- case OP_31_XOP_DCBST:
- case OP_31_XOP_DCBF:
- case OP_31_XOP_DCBI:
- /* Do nothing. The guest is performing dcbi because
- * hardware DMA is not snooped by the dcache, but
- * emulated DMA either goes through the dcache as
- * normal writes, or the host kernel has handled dcache
- * coherence. */
- break;
-
- case OP_31_XOP_LWBRX:
- emulated = kvmppc_handle_load(run, vcpu, rt, 4, 0);
- break;
-
- case OP_31_XOP_STWBRX:
- emulated = kvmppc_handle_store(run, vcpu,
- kvmppc_get_gpr(vcpu, rs), 4, 0);
break;
-
- case OP_31_XOP_LHBRX:
- emulated = kvmppc_handle_load(run, vcpu, rt, 2, 0);
- break;
-
- case OP_31_XOP_STHBRX:
- emulated = kvmppc_handle_store(run, vcpu,
- kvmppc_get_gpr(vcpu, rs), 2, 0);
- break;
-
- case OP_31_XOP_LDBRX:
- emulated = kvmppc_handle_load(run, vcpu, rt, 8, 0);
- break;
-
- case OP_31_XOP_STDBRX:
- emulated = kvmppc_handle_store(run, vcpu,
- kvmppc_get_gpr(vcpu, rs), 8, 0);
- break;
-
- case OP_31_XOP_LDX:
- emulated = kvmppc_handle_load(run, vcpu, rt, 8, 1);
- break;
-
- case OP_31_XOP_LDUX:
- emulated = kvmppc_handle_load(run, vcpu, rt, 8, 1);
- kvmppc_set_gpr(vcpu, ra, vcpu->arch.vaddr_accessed);
- break;
-
- case OP_31_XOP_LWAX:
- emulated = kvmppc_handle_loads(run, vcpu, rt, 4, 1);
- break;
-
- case OP_31_XOP_LWAUX:
- emulated = kvmppc_handle_loads(run, vcpu, rt, 4, 1);
- kvmppc_set_gpr(vcpu, ra, vcpu->arch.vaddr_accessed);
- break;
-
+ }
#ifdef CONFIG_PPC_FPU
- case OP_31_XOP_LFSX:
- if (kvmppc_check_fp_disabled(vcpu))
- return EMULATE_DONE;
- vcpu->arch.mmio_sp64_extend = 1;
- emulated = kvmppc_handle_load(run, vcpu,
- KVM_MMIO_REG_FPR|rt, 4, 1);
- break;
-
- case OP_31_XOP_LFSUX:
+ case LOAD_FP:
if (kvmppc_check_fp_disabled(vcpu))
return EMULATE_DONE;
- vcpu->arch.mmio_sp64_extend = 1;
- emulated = kvmppc_handle_load(run, vcpu,
- KVM_MMIO_REG_FPR|rt, 4, 1);
- kvmppc_set_gpr(vcpu, ra, vcpu->arch.vaddr_accessed);
- break;
- case OP_31_XOP_LFDX:
- if (kvmppc_check_fp_disabled(vcpu))
- return EMULATE_DONE;
- emulated = kvmppc_handle_load(run, vcpu,
- KVM_MMIO_REG_FPR|rt, 8, 1);
- break;
+ if (op.type & FPCONV)
+ vcpu->arch.mmio_sp64_extend = 1;
- case OP_31_XOP_LFDUX:
- if (kvmppc_check_fp_disabled(vcpu))
- return EMULATE_DONE;
- emulated = kvmppc_handle_load(run, vcpu,
- KVM_MMIO_REG_FPR|rt, 8, 1);
- kvmppc_set_gpr(vcpu, ra, vcpu->arch.vaddr_accessed);
- break;
-
- case OP_31_XOP_LFIWAX:
- if (kvmppc_check_fp_disabled(vcpu))
- return EMULATE_DONE;
- emulated = kvmppc_handle_loads(run, vcpu,
- KVM_MMIO_REG_FPR|rt, 4, 1);
- break;
+ if (op.type & SIGNEXT)
+ emulated = kvmppc_handle_loads(run, vcpu,
+ KVM_MMIO_REG_FPR|op.reg, size, 1);
+ else
+ emulated = kvmppc_handle_load(run, vcpu,
+ KVM_MMIO_REG_FPR|op.reg, size, 1);
- case OP_31_XOP_LFIWZX:
- if (kvmppc_check_fp_disabled(vcpu))
- return EMULATE_DONE;
- emulated = kvmppc_handle_load(run, vcpu,
- KVM_MMIO_REG_FPR|rt, 4, 1);
- break;
+ if ((op.type & UPDATE) && (emulated != EMULATE_FAIL))
+ kvmppc_set_gpr(vcpu, op.update_reg, op.ea);
- case OP_31_XOP_STFSX:
- if (kvmppc_check_fp_disabled(vcpu))
- return EMULATE_DONE;
- vcpu->arch.mmio_sp64_extend = 1;
- emulated = kvmppc_handle_store(run, vcpu,
- VCPU_FPR(vcpu, rs), 4, 1);
break;
-
- case OP_31_XOP_STFSUX:
- if (kvmppc_check_fp_disabled(vcpu))
- return EMULATE_DONE;
- vcpu->arch.mmio_sp64_extend = 1;
- emulated = kvmppc_handle_store(run, vcpu,
- VCPU_FPR(vcpu, rs), 4, 1);
- kvmppc_set_gpr(vcpu, ra, vcpu->arch.vaddr_accessed);
- break;
-
- case OP_31_XOP_STFDX:
- if (kvmppc_check_fp_disabled(vcpu))
- return EMULATE_DONE;
- emulated = kvmppc_handle_store(run, vcpu,
- VCPU_FPR(vcpu, rs), 8, 1);
- break;
-
- case OP_31_XOP_STFDUX:
- if (kvmppc_check_fp_disabled(vcpu))
+#endif
+#ifdef CONFIG_ALTIVEC
+ case LOAD_VMX:
+ if (kvmppc_check_altivec_disabled(vcpu))
return EMULATE_DONE;
- emulated = kvmppc_handle_store(run, vcpu,
- VCPU_FPR(vcpu, rs), 8, 1);
- kvmppc_set_gpr(vcpu, ra, vcpu->arch.vaddr_accessed);
- break;
- case OP_31_XOP_STFIWX:
- if (kvmppc_check_fp_disabled(vcpu))
- return EMULATE_DONE;
- emulated = kvmppc_handle_store(run, vcpu,
- VCPU_FPR(vcpu, rs), 4, 1);
+ /* Hardware enforces alignment of VMX accesses */
+ vcpu->arch.vaddr_accessed &= ~((unsigned long)size - 1);
+ vcpu->arch.paddr_accessed &= ~((unsigned long)size - 1);
+
+ if (size == 16) { /* lvx */
+ vcpu->arch.mmio_copy_type =
+ KVMPPC_VMX_COPY_DWORD;
+ } else if (size == 4) { /* lvewx */
+ vcpu->arch.mmio_copy_type =
+ KVMPPC_VMX_COPY_WORD;
+ } else if (size == 2) { /* lvehx */
+ vcpu->arch.mmio_copy_type =
+ KVMPPC_VMX_COPY_HWORD;
+ } else if (size == 1) { /* lvebx */
+ vcpu->arch.mmio_copy_type =
+ KVMPPC_VMX_COPY_BYTE;
+ } else
+ break;
+
+ vcpu->arch.mmio_vmx_offset =
+ (vcpu->arch.vaddr_accessed & 0xf)/size;
+
+ if (size == 16) {
+ vcpu->arch.mmio_vmx_copy_nums = 2;
+ emulated = kvmppc_handle_vmx_load(run,
+ vcpu, KVM_MMIO_REG_VMX|op.reg,
+ 8, 1);
+ } else {
+ vcpu->arch.mmio_vmx_copy_nums = 1;
+ emulated = kvmppc_handle_vmx_load(run, vcpu,
+ KVM_MMIO_REG_VMX|op.reg,
+ size, 1);
+ }
break;
#endif
-
#ifdef CONFIG_VSX
- case OP_31_XOP_LXSDX:
- if (kvmppc_check_vsx_disabled(vcpu))
- return EMULATE_DONE;
- vcpu->arch.mmio_vsx_copy_nums = 1;
- vcpu->arch.mmio_vsx_copy_type = KVMPPC_VSX_COPY_DWORD;
- emulated = kvmppc_handle_vsx_load(run, vcpu,
- KVM_MMIO_REG_VSX|rt, 8, 1, 0);
- break;
-
- case OP_31_XOP_LXSSPX:
- if (kvmppc_check_vsx_disabled(vcpu))
- return EMULATE_DONE;
- vcpu->arch.mmio_vsx_copy_nums = 1;
- vcpu->arch.mmio_vsx_copy_type = KVMPPC_VSX_COPY_DWORD;
- vcpu->arch.mmio_sp64_extend = 1;
- emulated = kvmppc_handle_vsx_load(run, vcpu,
- KVM_MMIO_REG_VSX|rt, 4, 1, 0);
- break;
+ case LOAD_VSX: {
+ int io_size_each;
+
+ if (op.vsx_flags & VSX_CHECK_VEC) {
+ if (kvmppc_check_altivec_disabled(vcpu))
+ return EMULATE_DONE;
+ } else {
+ if (kvmppc_check_vsx_disabled(vcpu))
+ return EMULATE_DONE;
+ }
+
+ if (op.vsx_flags & VSX_FPCONV)
+ vcpu->arch.mmio_sp64_extend = 1;
+
+ if (op.element_size == 8) {
+ if (op.vsx_flags & VSX_SPLAT)
+ vcpu->arch.mmio_copy_type =
+ KVMPPC_VSX_COPY_DWORD_LOAD_DUMP;
+ else
+ vcpu->arch.mmio_copy_type =
+ KVMPPC_VSX_COPY_DWORD;
+ } else if (op.element_size == 4) {
+ if (op.vsx_flags & VSX_SPLAT)
+ vcpu->arch.mmio_copy_type =
+ KVMPPC_VSX_COPY_WORD_LOAD_DUMP;
+ else
+ vcpu->arch.mmio_copy_type =
+ KVMPPC_VSX_COPY_WORD;
+ } else
+ break;
+
+ if (size < op.element_size) {
+ /* precision convert case: lxsspx, etc */
+ vcpu->arch.mmio_vsx_copy_nums = 1;
+ io_size_each = size;
+ } else { /* lxvw4x, lxvd2x, etc */
+ vcpu->arch.mmio_vsx_copy_nums =
+ size/op.element_size;
+ io_size_each = op.element_size;
+ }
- case OP_31_XOP_LXSIWAX:
- if (kvmppc_check_vsx_disabled(vcpu))
- return EMULATE_DONE;
- vcpu->arch.mmio_vsx_copy_nums = 1;
- vcpu->arch.mmio_vsx_copy_type = KVMPPC_VSX_COPY_DWORD;
emulated = kvmppc_handle_vsx_load(run, vcpu,
- KVM_MMIO_REG_VSX|rt, 4, 1, 1);
- break;
-
- case OP_31_XOP_LXSIWZX:
- if (kvmppc_check_vsx_disabled(vcpu))
- return EMULATE_DONE;
- vcpu->arch.mmio_vsx_copy_nums = 1;
- vcpu->arch.mmio_vsx_copy_type = KVMPPC_VSX_COPY_DWORD;
- emulated = kvmppc_handle_vsx_load(run, vcpu,
- KVM_MMIO_REG_VSX|rt, 4, 1, 0);
+ KVM_MMIO_REG_VSX | (op.reg & 0x1f),
+ io_size_each, 1, op.type & SIGNEXT);
break;
+ }
+#endif
+ case STORE:
+ /* if need byte reverse, op.val has been reversed by
+ * analyse_instr().
+ */
+ emulated = kvmppc_handle_store(run, vcpu, op.val,
+ size, 1);
- case OP_31_XOP_LXVD2X:
- /*
- * In this case, the official load/store process is like this:
- * Step1, exit from vm by page fault isr, then kvm save vsr.
- * Please see guest_exit_cont->store_fp_state->SAVE_32VSRS
- * as reference.
- *
- * Step2, copy data between memory and VCPU
- * Notice: for LXVD2X/STXVD2X/LXVW4X/STXVW4X, we use
- * 2copies*8bytes or 4copies*4bytes
- * to simulate one copy of 16bytes.
- * Also there is an endian issue here, we should notice the
- * layout of memory.
- * Please see MARCO of LXVD2X_ROT/STXVD2X_ROT as more reference.
- * If host is little-endian, kvm will call XXSWAPD for
- * LXVD2X_ROT/STXVD2X_ROT.
- * So, if host is little-endian,
- * the postion of memeory should be swapped.
- *
- * Step3, return to guest, kvm reset register.
- * Please see kvmppc_hv_entry->load_fp_state->REST_32VSRS
- * as reference.
- */
- if (kvmppc_check_vsx_disabled(vcpu))
- return EMULATE_DONE;
- vcpu->arch.mmio_vsx_copy_nums = 2;
- vcpu->arch.mmio_vsx_copy_type = KVMPPC_VSX_COPY_DWORD;
- emulated = kvmppc_handle_vsx_load(run, vcpu,
- KVM_MMIO_REG_VSX|rt, 8, 1, 0);
- break;
+ if ((op.type & UPDATE) && (emulated != EMULATE_FAIL))
+ kvmppc_set_gpr(vcpu, op.update_reg, op.ea);
- case OP_31_XOP_LXVW4X:
- if (kvmppc_check_vsx_disabled(vcpu))
- return EMULATE_DONE;
- vcpu->arch.mmio_vsx_copy_nums = 4;
- vcpu->arch.mmio_vsx_copy_type = KVMPPC_VSX_COPY_WORD;
- emulated = kvmppc_handle_vsx_load(run, vcpu,
- KVM_MMIO_REG_VSX|rt, 4, 1, 0);
break;
-
- case OP_31_XOP_LXVDSX:
- if (kvmppc_check_vsx_disabled(vcpu))
+#ifdef CONFIG_PPC_FPU
+ case STORE_FP:
+ if (kvmppc_check_fp_disabled(vcpu))
return EMULATE_DONE;
- vcpu->arch.mmio_vsx_copy_nums = 1;
- vcpu->arch.mmio_vsx_copy_type =
- KVMPPC_VSX_COPY_DWORD_LOAD_DUMP;
- emulated = kvmppc_handle_vsx_load(run, vcpu,
- KVM_MMIO_REG_VSX|rt, 8, 1, 0);
- break;
- case OP_31_XOP_STXSDX:
- if (kvmppc_check_vsx_disabled(vcpu))
- return EMULATE_DONE;
- vcpu->arch.mmio_vsx_copy_nums = 1;
- vcpu->arch.mmio_vsx_copy_type = KVMPPC_VSX_COPY_DWORD;
- emulated = kvmppc_handle_vsx_store(run, vcpu,
- rs, 8, 1);
- break;
+ /* The FP registers need to be flushed so that
+ * kvmppc_handle_store() can read actual FP vals
+ * from vcpu->arch.
+ */
+ if (vcpu->kvm->arch.kvm_ops->giveup_ext)
+ vcpu->kvm->arch.kvm_ops->giveup_ext(vcpu,
+ MSR_FP);
- case OP_31_XOP_STXSSPX:
- if (kvmppc_check_vsx_disabled(vcpu))
- return EMULATE_DONE;
- vcpu->arch.mmio_vsx_copy_nums = 1;
- vcpu->arch.mmio_vsx_copy_type = KVMPPC_VSX_COPY_DWORD;
- vcpu->arch.mmio_sp64_extend = 1;
- emulated = kvmppc_handle_vsx_store(run, vcpu,
- rs, 4, 1);
- break;
+ if (op.type & FPCONV)
+ vcpu->arch.mmio_sp64_extend = 1;
- case OP_31_XOP_STXSIWX:
- if (kvmppc_check_vsx_disabled(vcpu))
- return EMULATE_DONE;
- vcpu->arch.mmio_vsx_offset = 1;
- vcpu->arch.mmio_vsx_copy_nums = 1;
- vcpu->arch.mmio_vsx_copy_type = KVMPPC_VSX_COPY_WORD;
- emulated = kvmppc_handle_vsx_store(run, vcpu,
- rs, 4, 1);
- break;
+ emulated = kvmppc_handle_store(run, vcpu,
+ VCPU_FPR(vcpu, op.reg), size, 1);
- case OP_31_XOP_STXVD2X:
- if (kvmppc_check_vsx_disabled(vcpu))
- return EMULATE_DONE;
- vcpu->arch.mmio_vsx_copy_nums = 2;
- vcpu->arch.mmio_vsx_copy_type = KVMPPC_VSX_COPY_DWORD;
- emulated = kvmppc_handle_vsx_store(run, vcpu,
- rs, 8, 1);
- break;
+ if ((op.type & UPDATE) && (emulated != EMULATE_FAIL))
+ kvmppc_set_gpr(vcpu, op.update_reg, op.ea);
- case OP_31_XOP_STXVW4X:
- if (kvmppc_check_vsx_disabled(vcpu))
- return EMULATE_DONE;
- vcpu->arch.mmio_vsx_copy_nums = 4;
- vcpu->arch.mmio_vsx_copy_type = KVMPPC_VSX_COPY_WORD;
- emulated = kvmppc_handle_vsx_store(run, vcpu,
- rs, 4, 1);
break;
-#endif /* CONFIG_VSX */
-
+#endif
#ifdef CONFIG_ALTIVEC
- case OP_31_XOP_LVX:
+ case STORE_VMX:
if (kvmppc_check_altivec_disabled(vcpu))
return EMULATE_DONE;
- vcpu->arch.vaddr_accessed &= ~0xFULL;
- vcpu->arch.paddr_accessed &= ~0xFULL;
- vcpu->arch.mmio_vmx_copy_nums = 2;
- emulated = kvmppc_handle_load128_by2x64(run, vcpu,
- KVM_MMIO_REG_VMX|rt, 1);
- break;
- case OP_31_XOP_STVX:
- if (kvmppc_check_altivec_disabled(vcpu))
- return EMULATE_DONE;
- vcpu->arch.vaddr_accessed &= ~0xFULL;
- vcpu->arch.paddr_accessed &= ~0xFULL;
- vcpu->arch.mmio_vmx_copy_nums = 2;
- emulated = kvmppc_handle_store128_by2x64(run, vcpu,
- rs, 1);
- break;
-#endif /* CONFIG_ALTIVEC */
+ /* Hardware enforces alignment of VMX accesses. */
+ vcpu->arch.vaddr_accessed &= ~((unsigned long)size - 1);
+ vcpu->arch.paddr_accessed &= ~((unsigned long)size - 1);
+
+ if (vcpu->kvm->arch.kvm_ops->giveup_ext)
+ vcpu->kvm->arch.kvm_ops->giveup_ext(vcpu,
+ MSR_VEC);
+ if (size == 16) { /* stvx */
+ vcpu->arch.mmio_copy_type =
+ KVMPPC_VMX_COPY_DWORD;
+ } else if (size == 4) { /* stvewx */
+ vcpu->arch.mmio_copy_type =
+ KVMPPC_VMX_COPY_WORD;
+ } else if (size == 2) { /* stvehx */
+ vcpu->arch.mmio_copy_type =
+ KVMPPC_VMX_COPY_HWORD;
+ } else if (size == 1) { /* stvebx */
+ vcpu->arch.mmio_copy_type =
+ KVMPPC_VMX_COPY_BYTE;
+ } else
+ break;
+
+ vcpu->arch.mmio_vmx_offset =
+ (vcpu->arch.vaddr_accessed & 0xf)/size;
+
+ if (size == 16) {
+ vcpu->arch.mmio_vmx_copy_nums = 2;
+ emulated = kvmppc_handle_vmx_store(run,
+ vcpu, op.reg, 8, 1);
+ } else {
+ vcpu->arch.mmio_vmx_copy_nums = 1;
+ emulated = kvmppc_handle_vmx_store(run,
+ vcpu, op.reg, size, 1);
+ }
- default:
- emulated = EMULATE_FAIL;
break;
- }
- break;
-
- case OP_LWZ:
- emulated = kvmppc_handle_load(run, vcpu, rt, 4, 1);
- break;
-
-#ifdef CONFIG_PPC_FPU
- case OP_STFS:
- if (kvmppc_check_fp_disabled(vcpu))
- return EMULATE_DONE;
- vcpu->arch.mmio_sp64_extend = 1;
- emulated = kvmppc_handle_store(run, vcpu,
- VCPU_FPR(vcpu, rs),
- 4, 1);
- break;
-
- case OP_STFSU:
- if (kvmppc_check_fp_disabled(vcpu))
- return EMULATE_DONE;
- vcpu->arch.mmio_sp64_extend = 1;
- emulated = kvmppc_handle_store(run, vcpu,
- VCPU_FPR(vcpu, rs),
- 4, 1);
- kvmppc_set_gpr(vcpu, ra, vcpu->arch.vaddr_accessed);
- break;
-
- case OP_STFD:
- if (kvmppc_check_fp_disabled(vcpu))
- return EMULATE_DONE;
- emulated = kvmppc_handle_store(run, vcpu,
- VCPU_FPR(vcpu, rs),
- 8, 1);
- break;
-
- case OP_STFDU:
- if (kvmppc_check_fp_disabled(vcpu))
- return EMULATE_DONE;
- emulated = kvmppc_handle_store(run, vcpu,
- VCPU_FPR(vcpu, rs),
- 8, 1);
- kvmppc_set_gpr(vcpu, ra, vcpu->arch.vaddr_accessed);
- break;
#endif
+#ifdef CONFIG_VSX
+ case STORE_VSX: {
+ int io_size_each;
+
+ if (op.vsx_flags & VSX_CHECK_VEC) {
+ if (kvmppc_check_altivec_disabled(vcpu))
+ return EMULATE_DONE;
+ } else {
+ if (kvmppc_check_vsx_disabled(vcpu))
+ return EMULATE_DONE;
+ }
+
+ if (vcpu->kvm->arch.kvm_ops->giveup_ext)
+ vcpu->kvm->arch.kvm_ops->giveup_ext(vcpu,
+ MSR_VSX);
+
+ if (op.vsx_flags & VSX_FPCONV)
+ vcpu->arch.mmio_sp64_extend = 1;
+
+ if (op.element_size == 8)
+ vcpu->arch.mmio_copy_type =
+ KVMPPC_VSX_COPY_DWORD;
+ else if (op.element_size == 4)
+ vcpu->arch.mmio_copy_type =
+ KVMPPC_VSX_COPY_WORD;
+ else
+ break;
+
+ if (size < op.element_size) {
+ /* precise conversion case, like stxsspx */
+ vcpu->arch.mmio_vsx_copy_nums = 1;
+ io_size_each = size;
+ } else { /* stxvw4x, stxvd2x, etc */
+ vcpu->arch.mmio_vsx_copy_nums =
+ size/op.element_size;
+ io_size_each = op.element_size;
+ }
- case OP_LD:
- rt = get_rt(inst);
- switch (inst & 3) {
- case 0: /* ld */
- emulated = kvmppc_handle_load(run, vcpu, rt, 8, 1);
- break;
- case 1: /* ldu */
- emulated = kvmppc_handle_load(run, vcpu, rt, 8, 1);
- kvmppc_set_gpr(vcpu, ra, vcpu->arch.vaddr_accessed);
- break;
- case 2: /* lwa */
- emulated = kvmppc_handle_loads(run, vcpu, rt, 4, 1);
+ emulated = kvmppc_handle_vsx_store(run, vcpu,
+ op.reg & 0x1f, io_size_each, 1);
break;
- default:
- emulated = EMULATE_FAIL;
}
- break;
-
- case OP_LWZU:
- emulated = kvmppc_handle_load(run, vcpu, rt, 4, 1);
- kvmppc_set_gpr(vcpu, ra, vcpu->arch.vaddr_accessed);
- break;
-
- case OP_LBZ:
- emulated = kvmppc_handle_load(run, vcpu, rt, 1, 1);
- break;
-
- case OP_LBZU:
- emulated = kvmppc_handle_load(run, vcpu, rt, 1, 1);
- kvmppc_set_gpr(vcpu, ra, vcpu->arch.vaddr_accessed);
- break;
-
- case OP_STW:
- emulated = kvmppc_handle_store(run, vcpu,
- kvmppc_get_gpr(vcpu, rs),
- 4, 1);
- break;
-
- case OP_STD:
- rs = get_rs(inst);
- switch (inst & 3) {
- case 0: /* std */
- emulated = kvmppc_handle_store(run, vcpu,
- kvmppc_get_gpr(vcpu, rs), 8, 1);
- break;
- case 1: /* stdu */
- emulated = kvmppc_handle_store(run, vcpu,
- kvmppc_get_gpr(vcpu, rs), 8, 1);
- kvmppc_set_gpr(vcpu, ra, vcpu->arch.vaddr_accessed);
+#endif
+ case CACHEOP:
+ /* Do nothing. The guest is performing dcbi because
+ * hardware DMA is not snooped by the dcache, but
+ * emulated DMA either goes through the dcache as
+ * normal writes, or the host kernel has handled dcache
+ * coherence.
+ */
+ emulated = EMULATE_DONE;
break;
default:
- emulated = EMULATE_FAIL;
+ break;
}
- break;
-
- case OP_STWU:
- emulated = kvmppc_handle_store(run, vcpu,
- kvmppc_get_gpr(vcpu, rs), 4, 1);
- kvmppc_set_gpr(vcpu, ra, vcpu->arch.vaddr_accessed);
- break;
-
- case OP_STB:
- emulated = kvmppc_handle_store(run, vcpu,
- kvmppc_get_gpr(vcpu, rs), 1, 1);
- break;
-
- case OP_STBU:
- emulated = kvmppc_handle_store(run, vcpu,
- kvmppc_get_gpr(vcpu, rs), 1, 1);
- kvmppc_set_gpr(vcpu, ra, vcpu->arch.vaddr_accessed);
- break;
-
- case OP_LHZ:
- emulated = kvmppc_handle_load(run, vcpu, rt, 2, 1);
- break;
-
- case OP_LHZU:
- emulated = kvmppc_handle_load(run, vcpu, rt, 2, 1);
- kvmppc_set_gpr(vcpu, ra, vcpu->arch.vaddr_accessed);
- break;
-
- case OP_LHA:
- emulated = kvmppc_handle_loads(run, vcpu, rt, 2, 1);
- break;
-
- case OP_LHAU:
- emulated = kvmppc_handle_loads(run, vcpu, rt, 2, 1);
- kvmppc_set_gpr(vcpu, ra, vcpu->arch.vaddr_accessed);
- break;
-
- case OP_STH:
- emulated = kvmppc_handle_store(run, vcpu,
- kvmppc_get_gpr(vcpu, rs), 2, 1);
- break;
-
- case OP_STHU:
- emulated = kvmppc_handle_store(run, vcpu,
- kvmppc_get_gpr(vcpu, rs), 2, 1);
- kvmppc_set_gpr(vcpu, ra, vcpu->arch.vaddr_accessed);
- break;
-
-#ifdef CONFIG_PPC_FPU
- case OP_LFS:
- if (kvmppc_check_fp_disabled(vcpu))
- return EMULATE_DONE;
- vcpu->arch.mmio_sp64_extend = 1;
- emulated = kvmppc_handle_load(run, vcpu,
- KVM_MMIO_REG_FPR|rt, 4, 1);
- break;
-
- case OP_LFSU:
- if (kvmppc_check_fp_disabled(vcpu))
- return EMULATE_DONE;
- vcpu->arch.mmio_sp64_extend = 1;
- emulated = kvmppc_handle_load(run, vcpu,
- KVM_MMIO_REG_FPR|rt, 4, 1);
- kvmppc_set_gpr(vcpu, ra, vcpu->arch.vaddr_accessed);
- break;
-
- case OP_LFD:
- if (kvmppc_check_fp_disabled(vcpu))
- return EMULATE_DONE;
- emulated = kvmppc_handle_load(run, vcpu,
- KVM_MMIO_REG_FPR|rt, 8, 1);
- break;
-
- case OP_LFDU:
- if (kvmppc_check_fp_disabled(vcpu))
- return EMULATE_DONE;
- emulated = kvmppc_handle_load(run, vcpu,
- KVM_MMIO_REG_FPR|rt, 8, 1);
- kvmppc_set_gpr(vcpu, ra, vcpu->arch.vaddr_accessed);
- break;
-#endif
-
- default:
- emulated = EMULATE_FAIL;
- break;
}
if (emulated == EMULATE_FAIL) {
#endif
#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
case KVM_CAP_PPC_HTM:
- r = hv_enabled &&
- (!!(cur_cpu_spec->cpu_user_features2 & PPC_FEATURE2_HTM) ||
- cpu_has_feature(CPU_FTR_P9_TM_HV_ASSIST));
+ r = !!(cur_cpu_spec->cpu_user_features2 & PPC_FEATURE2_HTM) ||
+ (hv_enabled && cpu_has_feature(CPU_FTR_P9_TM_HV_ASSIST));
break;
#endif
default:
}
}
+static inline void kvmppc_set_vsr_word_dump(struct kvm_vcpu *vcpu,
+ u32 gpr)
+{
+ union kvmppc_one_reg val;
+ int index = vcpu->arch.io_gpr & KVM_MMIO_REG_MASK;
+
+ if (vcpu->arch.mmio_vsx_tx_sx_enabled) {
+ val.vsx32val[0] = gpr;
+ val.vsx32val[1] = gpr;
+ val.vsx32val[2] = gpr;
+ val.vsx32val[3] = gpr;
+ VCPU_VSX_VR(vcpu, index) = val.vval;
+ } else {
+ val.vsx32val[0] = gpr;
+ val.vsx32val[1] = gpr;
+ VCPU_VSX_FPR(vcpu, index, 0) = val.vsxval[0];
+ VCPU_VSX_FPR(vcpu, index, 1) = val.vsxval[0];
+ }
+}
+
static inline void kvmppc_set_vsr_word(struct kvm_vcpu *vcpu,
u32 gpr32)
{
#endif /* CONFIG_VSX */
#ifdef CONFIG_ALTIVEC
+static inline int kvmppc_get_vmx_offset_generic(struct kvm_vcpu *vcpu,
+ int index, int element_size)
+{
+ int offset;
+ int elts = sizeof(vector128)/element_size;
+
+ if ((index < 0) || (index >= elts))
+ return -1;
+
+ if (kvmppc_need_byteswap(vcpu))
+ offset = elts - index - 1;
+ else
+ offset = index;
+
+ return offset;
+}
+
+static inline int kvmppc_get_vmx_dword_offset(struct kvm_vcpu *vcpu,
+ int index)
+{
+ return kvmppc_get_vmx_offset_generic(vcpu, index, 8);
+}
+
+static inline int kvmppc_get_vmx_word_offset(struct kvm_vcpu *vcpu,
+ int index)
+{
+ return kvmppc_get_vmx_offset_generic(vcpu, index, 4);
+}
+
+static inline int kvmppc_get_vmx_hword_offset(struct kvm_vcpu *vcpu,
+ int index)
+{
+ return kvmppc_get_vmx_offset_generic(vcpu, index, 2);
+}
+
+static inline int kvmppc_get_vmx_byte_offset(struct kvm_vcpu *vcpu,
+ int index)
+{
+ return kvmppc_get_vmx_offset_generic(vcpu, index, 1);
+}
+
+
static inline void kvmppc_set_vmx_dword(struct kvm_vcpu *vcpu,
- u64 gpr)
+ u64 gpr)
{
+ union kvmppc_one_reg val;
+ int offset = kvmppc_get_vmx_dword_offset(vcpu,
+ vcpu->arch.mmio_vmx_offset);
int index = vcpu->arch.io_gpr & KVM_MMIO_REG_MASK;
- u32 hi, lo;
- u32 di;
-#ifdef __BIG_ENDIAN
- hi = gpr >> 32;
- lo = gpr & 0xffffffff;
-#else
- lo = gpr >> 32;
- hi = gpr & 0xffffffff;
-#endif
+ if (offset == -1)
+ return;
+
+ val.vval = VCPU_VSX_VR(vcpu, index);
+ val.vsxval[offset] = gpr;
+ VCPU_VSX_VR(vcpu, index) = val.vval;
+}
+
+static inline void kvmppc_set_vmx_word(struct kvm_vcpu *vcpu,
+ u32 gpr32)
+{
+ union kvmppc_one_reg val;
+ int offset = kvmppc_get_vmx_word_offset(vcpu,
+ vcpu->arch.mmio_vmx_offset);
+ int index = vcpu->arch.io_gpr & KVM_MMIO_REG_MASK;
- di = 2 - vcpu->arch.mmio_vmx_copy_nums; /* doubleword index */
- if (di > 1)
+ if (offset == -1)
return;
- if (vcpu->arch.mmio_host_swabbed)
- di = 1 - di;
+ val.vval = VCPU_VSX_VR(vcpu, index);
+ val.vsx32val[offset] = gpr32;
+ VCPU_VSX_VR(vcpu, index) = val.vval;
+}
+
+static inline void kvmppc_set_vmx_hword(struct kvm_vcpu *vcpu,
+ u16 gpr16)
+{
+ union kvmppc_one_reg val;
+ int offset = kvmppc_get_vmx_hword_offset(vcpu,
+ vcpu->arch.mmio_vmx_offset);
+ int index = vcpu->arch.io_gpr & KVM_MMIO_REG_MASK;
+
+ if (offset == -1)
+ return;
+
+ val.vval = VCPU_VSX_VR(vcpu, index);
+ val.vsx16val[offset] = gpr16;
+ VCPU_VSX_VR(vcpu, index) = val.vval;
+}
+
+static inline void kvmppc_set_vmx_byte(struct kvm_vcpu *vcpu,
+ u8 gpr8)
+{
+ union kvmppc_one_reg val;
+ int offset = kvmppc_get_vmx_byte_offset(vcpu,
+ vcpu->arch.mmio_vmx_offset);
+ int index = vcpu->arch.io_gpr & KVM_MMIO_REG_MASK;
+
+ if (offset == -1)
+ return;
- VCPU_VSX_VR(vcpu, index).u[di * 2] = hi;
- VCPU_VSX_VR(vcpu, index).u[di * 2 + 1] = lo;
+ val.vval = VCPU_VSX_VR(vcpu, index);
+ val.vsx8val[offset] = gpr8;
+ VCPU_VSX_VR(vcpu, index) = val.vval;
}
#endif /* CONFIG_ALTIVEC */
kvmppc_set_gpr(vcpu, vcpu->arch.io_gpr, gpr);
break;
case KVM_MMIO_REG_FPR:
+ if (vcpu->kvm->arch.kvm_ops->giveup_ext)
+ vcpu->kvm->arch.kvm_ops->giveup_ext(vcpu, MSR_FP);
+
VCPU_FPR(vcpu, vcpu->arch.io_gpr & KVM_MMIO_REG_MASK) = gpr;
break;
#ifdef CONFIG_PPC_BOOK3S
#endif
#ifdef CONFIG_VSX
case KVM_MMIO_REG_VSX:
- if (vcpu->arch.mmio_vsx_copy_type == KVMPPC_VSX_COPY_DWORD)
+ if (vcpu->kvm->arch.kvm_ops->giveup_ext)
+ vcpu->kvm->arch.kvm_ops->giveup_ext(vcpu, MSR_VSX);
+
+ if (vcpu->arch.mmio_copy_type == KVMPPC_VSX_COPY_DWORD)
kvmppc_set_vsr_dword(vcpu, gpr);
- else if (vcpu->arch.mmio_vsx_copy_type == KVMPPC_VSX_COPY_WORD)
+ else if (vcpu->arch.mmio_copy_type == KVMPPC_VSX_COPY_WORD)
kvmppc_set_vsr_word(vcpu, gpr);
- else if (vcpu->arch.mmio_vsx_copy_type ==
+ else if (vcpu->arch.mmio_copy_type ==
KVMPPC_VSX_COPY_DWORD_LOAD_DUMP)
kvmppc_set_vsr_dword_dump(vcpu, gpr);
+ else if (vcpu->arch.mmio_copy_type ==
+ KVMPPC_VSX_COPY_WORD_LOAD_DUMP)
+ kvmppc_set_vsr_word_dump(vcpu, gpr);
break;
#endif
#ifdef CONFIG_ALTIVEC
case KVM_MMIO_REG_VMX:
- kvmppc_set_vmx_dword(vcpu, gpr);
+ if (vcpu->kvm->arch.kvm_ops->giveup_ext)
+ vcpu->kvm->arch.kvm_ops->giveup_ext(vcpu, MSR_VEC);
+
+ if (vcpu->arch.mmio_copy_type == KVMPPC_VMX_COPY_DWORD)
+ kvmppc_set_vmx_dword(vcpu, gpr);
+ else if (vcpu->arch.mmio_copy_type == KVMPPC_VMX_COPY_WORD)
+ kvmppc_set_vmx_word(vcpu, gpr);
+ else if (vcpu->arch.mmio_copy_type ==
+ KVMPPC_VMX_COPY_HWORD)
+ kvmppc_set_vmx_hword(vcpu, gpr);
+ else if (vcpu->arch.mmio_copy_type ==
+ KVMPPC_VMX_COPY_BYTE)
+ kvmppc_set_vmx_byte(vcpu, gpr);
break;
#endif
default:
u32 dword_offset, word_offset;
union kvmppc_one_reg reg;
int vsx_offset = 0;
- int copy_type = vcpu->arch.mmio_vsx_copy_type;
+ int copy_type = vcpu->arch.mmio_copy_type;
int result = 0;
switch (copy_type) {
#endif /* CONFIG_VSX */
#ifdef CONFIG_ALTIVEC
-/* handle quadword load access in two halves */
-int kvmppc_handle_load128_by2x64(struct kvm_run *run, struct kvm_vcpu *vcpu,
- unsigned int rt, int is_default_endian)
+int kvmppc_handle_vmx_load(struct kvm_run *run, struct kvm_vcpu *vcpu,
+ unsigned int rt, unsigned int bytes, int is_default_endian)
{
enum emulation_result emulated = EMULATE_DONE;
+ if (vcpu->arch.mmio_vsx_copy_nums > 2)
+ return EMULATE_FAIL;
+
while (vcpu->arch.mmio_vmx_copy_nums) {
- emulated = __kvmppc_handle_load(run, vcpu, rt, 8,
+ emulated = __kvmppc_handle_load(run, vcpu, rt, bytes,
is_default_endian, 0);
if (emulated != EMULATE_DONE)
vcpu->arch.paddr_accessed += run->mmio.len;
vcpu->arch.mmio_vmx_copy_nums--;
+ vcpu->arch.mmio_vmx_offset++;
}
return emulated;
}
-static inline int kvmppc_get_vmx_data(struct kvm_vcpu *vcpu, int rs, u64 *val)
+int kvmppc_get_vmx_dword(struct kvm_vcpu *vcpu, int index, u64 *val)
{
- vector128 vrs = VCPU_VSX_VR(vcpu, rs);
- u32 di;
- u64 w0, w1;
+ union kvmppc_one_reg reg;
+ int vmx_offset = 0;
+ int result = 0;
- di = 2 - vcpu->arch.mmio_vmx_copy_nums; /* doubleword index */
- if (di > 1)
+ vmx_offset =
+ kvmppc_get_vmx_dword_offset(vcpu, vcpu->arch.mmio_vmx_offset);
+
+ if (vmx_offset == -1)
return -1;
- if (vcpu->arch.mmio_host_swabbed)
- di = 1 - di;
+ reg.vval = VCPU_VSX_VR(vcpu, index);
+ *val = reg.vsxval[vmx_offset];
- w0 = vrs.u[di * 2];
- w1 = vrs.u[di * 2 + 1];
+ return result;
+}
-#ifdef __BIG_ENDIAN
- *val = (w0 << 32) | w1;
-#else
- *val = (w1 << 32) | w0;
-#endif
- return 0;
+int kvmppc_get_vmx_word(struct kvm_vcpu *vcpu, int index, u64 *val)
+{
+ union kvmppc_one_reg reg;
+ int vmx_offset = 0;
+ int result = 0;
+
+ vmx_offset =
+ kvmppc_get_vmx_word_offset(vcpu, vcpu->arch.mmio_vmx_offset);
+
+ if (vmx_offset == -1)
+ return -1;
+
+ reg.vval = VCPU_VSX_VR(vcpu, index);
+ *val = reg.vsx32val[vmx_offset];
+
+ return result;
+}
+
+int kvmppc_get_vmx_hword(struct kvm_vcpu *vcpu, int index, u64 *val)
+{
+ union kvmppc_one_reg reg;
+ int vmx_offset = 0;
+ int result = 0;
+
+ vmx_offset =
+ kvmppc_get_vmx_hword_offset(vcpu, vcpu->arch.mmio_vmx_offset);
+
+ if (vmx_offset == -1)
+ return -1;
+
+ reg.vval = VCPU_VSX_VR(vcpu, index);
+ *val = reg.vsx16val[vmx_offset];
+
+ return result;
}
-/* handle quadword store in two halves */
-int kvmppc_handle_store128_by2x64(struct kvm_run *run, struct kvm_vcpu *vcpu,
- unsigned int rs, int is_default_endian)
+int kvmppc_get_vmx_byte(struct kvm_vcpu *vcpu, int index, u64 *val)
+{
+ union kvmppc_one_reg reg;
+ int vmx_offset = 0;
+ int result = 0;
+
+ vmx_offset =
+ kvmppc_get_vmx_byte_offset(vcpu, vcpu->arch.mmio_vmx_offset);
+
+ if (vmx_offset == -1)
+ return -1;
+
+ reg.vval = VCPU_VSX_VR(vcpu, index);
+ *val = reg.vsx8val[vmx_offset];
+
+ return result;
+}
+
+int kvmppc_handle_vmx_store(struct kvm_run *run, struct kvm_vcpu *vcpu,
+ unsigned int rs, unsigned int bytes, int is_default_endian)
{
u64 val = 0;
+ unsigned int index = rs & KVM_MMIO_REG_MASK;
enum emulation_result emulated = EMULATE_DONE;
+ if (vcpu->arch.mmio_vsx_copy_nums > 2)
+ return EMULATE_FAIL;
+
vcpu->arch.io_gpr = rs;
while (vcpu->arch.mmio_vmx_copy_nums) {
- if (kvmppc_get_vmx_data(vcpu, rs, &val) == -1)
+ switch (vcpu->arch.mmio_copy_type) {
+ case KVMPPC_VMX_COPY_DWORD:
+ if (kvmppc_get_vmx_dword(vcpu, index, &val) == -1)
+ return EMULATE_FAIL;
+
+ break;
+ case KVMPPC_VMX_COPY_WORD:
+ if (kvmppc_get_vmx_word(vcpu, index, &val) == -1)
+ return EMULATE_FAIL;
+ break;
+ case KVMPPC_VMX_COPY_HWORD:
+ if (kvmppc_get_vmx_hword(vcpu, index, &val) == -1)
+ return EMULATE_FAIL;
+ break;
+ case KVMPPC_VMX_COPY_BYTE:
+ if (kvmppc_get_vmx_byte(vcpu, index, &val) == -1)
+ return EMULATE_FAIL;
+ break;
+ default:
return EMULATE_FAIL;
+ }
- emulated = kvmppc_handle_store(run, vcpu, val, 8,
+ emulated = kvmppc_handle_store(run, vcpu, val, bytes,
is_default_endian);
if (emulated != EMULATE_DONE)
break;
vcpu->arch.paddr_accessed += run->mmio.len;
vcpu->arch.mmio_vmx_copy_nums--;
+ vcpu->arch.mmio_vmx_offset++;
}
return emulated;
vcpu->arch.paddr_accessed += run->mmio.len;
if (!vcpu->mmio_is_write) {
- emulated = kvmppc_handle_load128_by2x64(run, vcpu,
- vcpu->arch.io_gpr, 1);
+ emulated = kvmppc_handle_vmx_load(run, vcpu,
+ vcpu->arch.io_gpr, run->mmio.len, 1);
} else {
- emulated = kvmppc_handle_store128_by2x64(run, vcpu,
- vcpu->arch.io_gpr, 1);
+ emulated = kvmppc_handle_vmx_store(run, vcpu,
+ vcpu->arch.io_gpr, run->mmio.len, 1);
}
switch (emulated) {
}
#endif
#ifdef CONFIG_ALTIVEC
- if (vcpu->arch.mmio_vmx_copy_nums > 0)
+ if (vcpu->arch.mmio_vmx_copy_nums > 0) {
vcpu->arch.mmio_vmx_copy_nums--;
+ vcpu->arch.mmio_vmx_offset++;
+ }
if (vcpu->arch.mmio_vmx_copy_nums > 0) {
r = kvmppc_emulate_mmio_vmx_loadstore(vcpu, run);
void __user *argp = (void __user *)arg;
long r;
- vcpu_load(vcpu);
-
switch (ioctl) {
case KVM_ENABLE_CAP:
{
struct kvm_enable_cap cap;
r = -EFAULT;
+ vcpu_load(vcpu);
if (copy_from_user(&cap, argp, sizeof(cap)))
goto out;
r = kvm_vcpu_ioctl_enable_cap(vcpu, &cap);
+ vcpu_put(vcpu);
break;
}
case KVM_DIRTY_TLB: {
struct kvm_dirty_tlb dirty;
r = -EFAULT;
+ vcpu_load(vcpu);
if (copy_from_user(&dirty, argp, sizeof(dirty)))
goto out;
r = kvm_vcpu_ioctl_dirty_tlb(vcpu, &dirty);
+ vcpu_put(vcpu);
break;
}
#endif
}
out:
- vcpu_put(vcpu);
return r;
}
--- /dev/null
+/*
+ * 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.
+ *
+ * Derived from book3s_hv_rmhandlers.S, which is:
+ *
+ * Copyright 2011 Paul Mackerras, IBM Corp. <paulus@au1.ibm.com>
+ *
+ */
+
+#include <asm/reg.h>
+#include <asm/ppc_asm.h>
+#include <asm/asm-offsets.h>
+#include <asm/export.h>
+#include <asm/tm.h>
+#include <asm/cputable.h>
+
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+#define VCPU_GPRS_TM(reg) (((reg) * ULONG_SIZE) + VCPU_GPR_TM)
+
+/*
+ * Save transactional state and TM-related registers.
+ * Called with:
+ * - r3 pointing to the vcpu struct
+ * - r4 points to the MSR with current TS bits:
+ * (For HV KVM, it is VCPU_MSR ; For PR KVM, it is host MSR).
+ * This can modify all checkpointed registers, but
+ * restores r1, r2 before exit.
+ */
+_GLOBAL(__kvmppc_save_tm)
+ mflr r0
+ std r0, PPC_LR_STKOFF(r1)
+
+ /* Turn on TM. */
+ mfmsr r8
+ li r0, 1
+ rldimi r8, r0, MSR_TM_LG, 63-MSR_TM_LG
+ ori r8, r8, MSR_FP
+ oris r8, r8, (MSR_VEC | MSR_VSX)@h
+ mtmsrd r8
+
+ rldicl. r4, r4, 64 - MSR_TS_S_LG, 62
+ beq 1f /* TM not active in guest. */
+
+ std r1, HSTATE_SCRATCH2(r13)
+ std r3, HSTATE_SCRATCH1(r13)
+
+#ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE
+BEGIN_FTR_SECTION
+ /* Emulation of the treclaim instruction needs TEXASR before treclaim */
+ mfspr r6, SPRN_TEXASR
+ std r6, VCPU_ORIG_TEXASR(r3)
+END_FTR_SECTION_IFSET(CPU_FTR_P9_TM_HV_ASSIST)
+#endif
+
+ /* Clear the MSR RI since r1, r13 are all going to be foobar. */
+ li r5, 0
+ mtmsrd r5, 1
+
+ li r3, TM_CAUSE_KVM_RESCHED
+
+ /* All GPRs are volatile at this point. */
+ TRECLAIM(R3)
+
+ /* Temporarily store r13 and r9 so we have some regs to play with */
+ SET_SCRATCH0(r13)
+ GET_PACA(r13)
+ std r9, PACATMSCRATCH(r13)
+ ld r9, HSTATE_SCRATCH1(r13)
+
+ /* Get a few more GPRs free. */
+ std r29, VCPU_GPRS_TM(29)(r9)
+ std r30, VCPU_GPRS_TM(30)(r9)
+ std r31, VCPU_GPRS_TM(31)(r9)
+
+ /* Save away PPR and DSCR soon so don't run with user values. */
+ mfspr r31, SPRN_PPR
+ HMT_MEDIUM
+ mfspr r30, SPRN_DSCR
+#ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE
+ ld r29, HSTATE_DSCR(r13)
+ mtspr SPRN_DSCR, r29
+#endif
+
+ /* Save all but r9, r13 & r29-r31 */
+ reg = 0
+ .rept 29
+ .if (reg != 9) && (reg != 13)
+ std reg, VCPU_GPRS_TM(reg)(r9)
+ .endif
+ reg = reg + 1
+ .endr
+ /* ... now save r13 */
+ GET_SCRATCH0(r4)
+ std r4, VCPU_GPRS_TM(13)(r9)
+ /* ... and save r9 */
+ ld r4, PACATMSCRATCH(r13)
+ std r4, VCPU_GPRS_TM(9)(r9)
+
+ /* Reload stack pointer and TOC. */
+ ld r1, HSTATE_SCRATCH2(r13)
+ ld r2, PACATOC(r13)
+
+ /* Set MSR RI now we have r1 and r13 back. */
+ li r5, MSR_RI
+ mtmsrd r5, 1
+
+ /* Save away checkpinted SPRs. */
+ std r31, VCPU_PPR_TM(r9)
+ std r30, VCPU_DSCR_TM(r9)
+ mflr r5
+ mfcr r6
+ mfctr r7
+ mfspr r8, SPRN_AMR
+ mfspr r10, SPRN_TAR
+ mfxer r11
+ std r5, VCPU_LR_TM(r9)
+ stw r6, VCPU_CR_TM(r9)
+ std r7, VCPU_CTR_TM(r9)
+ std r8, VCPU_AMR_TM(r9)
+ std r10, VCPU_TAR_TM(r9)
+ std r11, VCPU_XER_TM(r9)
+
+ /* Restore r12 as trap number. */
+ lwz r12, VCPU_TRAP(r9)
+
+ /* Save FP/VSX. */
+ addi r3, r9, VCPU_FPRS_TM
+ bl store_fp_state
+ addi r3, r9, VCPU_VRS_TM
+ bl store_vr_state
+ mfspr r6, SPRN_VRSAVE
+ stw r6, VCPU_VRSAVE_TM(r9)
+1:
+ /*
+ * We need to save these SPRs after the treclaim so that the software
+ * error code is recorded correctly in the TEXASR. Also the user may
+ * change these outside of a transaction, so they must always be
+ * context switched.
+ */
+ mfspr r7, SPRN_TEXASR
+ std r7, VCPU_TEXASR(r9)
+11:
+ mfspr r5, SPRN_TFHAR
+ mfspr r6, SPRN_TFIAR
+ std r5, VCPU_TFHAR(r9)
+ std r6, VCPU_TFIAR(r9)
+
+ ld r0, PPC_LR_STKOFF(r1)
+ mtlr r0
+ blr
+
+/*
+ * _kvmppc_save_tm_pr() is a wrapper around __kvmppc_save_tm(), so that it can
+ * be invoked from C function by PR KVM only.
+ */
+_GLOBAL(_kvmppc_save_tm_pr)
+ mflr r5
+ std r5, PPC_LR_STKOFF(r1)
+ stdu r1, -SWITCH_FRAME_SIZE(r1)
+ SAVE_NVGPRS(r1)
+
+ /* save MSR since TM/math bits might be impacted
+ * by __kvmppc_save_tm().
+ */
+ mfmsr r5
+ SAVE_GPR(5, r1)
+
+ /* also save DSCR/CR/TAR so that it can be recovered later */
+ mfspr r6, SPRN_DSCR
+ SAVE_GPR(6, r1)
+
+ mfcr r7
+ stw r7, _CCR(r1)
+
+ mfspr r8, SPRN_TAR
+ SAVE_GPR(8, r1)
+
+ bl __kvmppc_save_tm
+
+ REST_GPR(8, r1)
+ mtspr SPRN_TAR, r8
+
+ ld r7, _CCR(r1)
+ mtcr r7
+
+ REST_GPR(6, r1)
+ mtspr SPRN_DSCR, r6
+
+ /* need preserve current MSR's MSR_TS bits */
+ REST_GPR(5, r1)
+ mfmsr r6
+ rldicl r6, r6, 64 - MSR_TS_S_LG, 62
+ rldimi r5, r6, MSR_TS_S_LG, 63 - MSR_TS_T_LG
+ mtmsrd r5
+
+ REST_NVGPRS(r1)
+ addi r1, r1, SWITCH_FRAME_SIZE
+ ld r5, PPC_LR_STKOFF(r1)
+ mtlr r5
+ blr
+
+EXPORT_SYMBOL_GPL(_kvmppc_save_tm_pr);
+
+/*
+ * Restore transactional state and TM-related registers.
+ * Called with:
+ * - r3 pointing to the vcpu struct.
+ * - r4 is the guest MSR with desired TS bits:
+ * For HV KVM, it is VCPU_MSR
+ * For PR KVM, it is provided by caller
+ * This potentially modifies all checkpointed registers.
+ * It restores r1, r2 from the PACA.
+ */
+_GLOBAL(__kvmppc_restore_tm)
+ mflr r0
+ std r0, PPC_LR_STKOFF(r1)
+
+ /* Turn on TM/FP/VSX/VMX so we can restore them. */
+ mfmsr r5
+ li r6, MSR_TM >> 32
+ sldi r6, r6, 32
+ or r5, r5, r6
+ ori r5, r5, MSR_FP
+ oris r5, r5, (MSR_VEC | MSR_VSX)@h
+ mtmsrd r5
+
+ /*
+ * The user may change these outside of a transaction, so they must
+ * always be context switched.
+ */
+ ld r5, VCPU_TFHAR(r3)
+ ld r6, VCPU_TFIAR(r3)
+ ld r7, VCPU_TEXASR(r3)
+ mtspr SPRN_TFHAR, r5
+ mtspr SPRN_TFIAR, r6
+ mtspr SPRN_TEXASR, r7
+
+ mr r5, r4
+ rldicl. r5, r5, 64 - MSR_TS_S_LG, 62
+ beqlr /* TM not active in guest */
+ std r1, HSTATE_SCRATCH2(r13)
+
+ /* Make sure the failure summary is set, otherwise we'll program check
+ * when we trechkpt. It's possible that this might have been not set
+ * on a kvmppc_set_one_reg() call but we shouldn't let this crash the
+ * host.
+ */
+ oris r7, r7, (TEXASR_FS)@h
+ mtspr SPRN_TEXASR, r7
+
+ /*
+ * We need to load up the checkpointed state for the guest.
+ * We need to do this early as it will blow away any GPRs, VSRs and
+ * some SPRs.
+ */
+
+ mr r31, r3
+ addi r3, r31, VCPU_FPRS_TM
+ bl load_fp_state
+ addi r3, r31, VCPU_VRS_TM
+ bl load_vr_state
+ mr r3, r31
+ lwz r7, VCPU_VRSAVE_TM(r3)
+ mtspr SPRN_VRSAVE, r7
+
+ ld r5, VCPU_LR_TM(r3)
+ lwz r6, VCPU_CR_TM(r3)
+ ld r7, VCPU_CTR_TM(r3)
+ ld r8, VCPU_AMR_TM(r3)
+ ld r9, VCPU_TAR_TM(r3)
+ ld r10, VCPU_XER_TM(r3)
+ mtlr r5
+ mtcr r6
+ mtctr r7
+ mtspr SPRN_AMR, r8
+ mtspr SPRN_TAR, r9
+ mtxer r10
+
+ /*
+ * Load up PPR and DSCR values but don't put them in the actual SPRs
+ * till the last moment to avoid running with userspace PPR and DSCR for
+ * too long.
+ */
+ ld r29, VCPU_DSCR_TM(r3)
+ ld r30, VCPU_PPR_TM(r3)
+
+ std r2, PACATMSCRATCH(r13) /* Save TOC */
+
+ /* Clear the MSR RI since r1, r13 are all going to be foobar. */
+ li r5, 0
+ mtmsrd r5, 1
+
+ /* Load GPRs r0-r28 */
+ reg = 0
+ .rept 29
+ ld reg, VCPU_GPRS_TM(reg)(r31)
+ reg = reg + 1
+ .endr
+
+ mtspr SPRN_DSCR, r29
+ mtspr SPRN_PPR, r30
+
+ /* Load final GPRs */
+ ld 29, VCPU_GPRS_TM(29)(r31)
+ ld 30, VCPU_GPRS_TM(30)(r31)
+ ld 31, VCPU_GPRS_TM(31)(r31)
+
+ /* TM checkpointed state is now setup. All GPRs are now volatile. */
+ TRECHKPT
+
+ /* Now let's get back the state we need. */
+ HMT_MEDIUM
+ GET_PACA(r13)
+#ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE
+ ld r29, HSTATE_DSCR(r13)
+ mtspr SPRN_DSCR, r29
+#endif
+ ld r1, HSTATE_SCRATCH2(r13)
+ ld r2, PACATMSCRATCH(r13)
+
+ /* Set the MSR RI since we have our registers back. */
+ li r5, MSR_RI
+ mtmsrd r5, 1
+ ld r0, PPC_LR_STKOFF(r1)
+ mtlr r0
+ blr
+
+/*
+ * _kvmppc_restore_tm_pr() is a wrapper around __kvmppc_restore_tm(), so that it
+ * can be invoked from C function by PR KVM only.
+ */
+_GLOBAL(_kvmppc_restore_tm_pr)
+ mflr r5
+ std r5, PPC_LR_STKOFF(r1)
+ stdu r1, -SWITCH_FRAME_SIZE(r1)
+ SAVE_NVGPRS(r1)
+
+ /* save MSR to avoid TM/math bits change */
+ mfmsr r5
+ SAVE_GPR(5, r1)
+
+ /* also save DSCR/CR/TAR so that it can be recovered later */
+ mfspr r6, SPRN_DSCR
+ SAVE_GPR(6, r1)
+
+ mfcr r7
+ stw r7, _CCR(r1)
+
+ mfspr r8, SPRN_TAR
+ SAVE_GPR(8, r1)
+
+ bl __kvmppc_restore_tm
+
+ REST_GPR(8, r1)
+ mtspr SPRN_TAR, r8
+
+ ld r7, _CCR(r1)
+ mtcr r7
+
+ REST_GPR(6, r1)
+ mtspr SPRN_DSCR, r6
+
+ /* need preserve current MSR's MSR_TS bits */
+ REST_GPR(5, r1)
+ mfmsr r6
+ rldicl r6, r6, 64 - MSR_TS_S_LG, 62
+ rldimi r5, r6, MSR_TS_S_LG, 63 - MSR_TS_T_LG
+ mtmsrd r5
+
+ REST_NVGPRS(r1)
+ addi r1, r1, SWITCH_FRAME_SIZE
+ ld r5, PPC_LR_STKOFF(r1)
+ mtlr r5
+ blr
+
+EXPORT_SYMBOL_GPL(_kvmppc_restore_tm_pr);
+#endif /* CONFIG_PPC_TRANSACTIONAL_MEM */
/* Place all memblock_regions in the same node and merge contiguous
* memblock_regions
*/
- memblock_set_node(0, (phys_addr_t)ULLONG_MAX, &memblock.memory, 0);
+ memblock_set_node(0, PHYS_ADDR_MAX, &memblock.memory, 0);
}
void __init initmem_init(void)
select HAVE_MEMBLOCK_NODE_MAP
select HAVE_DMA_CONTIGUOUS
select HAVE_GENERIC_DMA_COHERENT
+ select HAVE_PERF_EVENTS
select IRQ_DOMAIN
select NO_BOOTMEM
select RISCV_ISA_A if SMP
config RISCV_ISA_A
def_bool y
+menu "supported PMU type"
+ depends on PERF_EVENTS
+
+config RISCV_BASE_PMU
+ bool "Base Performance Monitoring Unit"
+ def_bool y
+ help
+ A base PMU that serves as a reference implementation and has limited
+ feature of perf. It can run on any RISC-V machines so serves as the
+ fallback, but this option can also be disable to reduce kernel size.
+
+endmenu
+
endmenu
menu "Kernel type"
# architectures. It's faster to have GCC emit only aligned accesses.
KBUILD_CFLAGS += $(call cc-option,-mstrict-align)
+# arch specific predefines for sparse
+CHECKFLAGS += -D__riscv -D__riscv_xlen=$(BITS)
+
head-y := arch/riscv/kernel/head.o
core-y += arch/riscv/kernel/ arch/riscv/mm/
CONFIG_SERIAL_8250=y
CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_SERIAL_OF_PLATFORM=y
+CONFIG_HVC_RISCV_SBI=y
# CONFIG_PTP_1588_CLOCK is not set
CONFIG_DRM=y
CONFIG_DRM_RADEON=y
generic-y += kmap_types.h
generic-y += kvm_para.h
generic-y += local.h
+generic-y += local64.h
generic-y += mm-arch-hooks.h
generic-y += mman.h
generic-y += module.h
#else /* CONFIG_SMP */
-#define flush_icache_all() sbi_remote_fence_i(0)
+#define flush_icache_all() sbi_remote_fence_i(NULL)
void flush_icache_mm(struct mm_struct *mm, bool local);
#endif /* CONFIG_SMP */
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2018 SiFive
+ * Copyright (C) 2018 Andes Technology Corporation
+ *
+ */
+
+#ifndef _ASM_RISCV_PERF_EVENT_H
+#define _ASM_RISCV_PERF_EVENT_H
+
+#include <linux/perf_event.h>
+#include <linux/ptrace.h>
+
+#define RISCV_BASE_COUNTERS 2
+
+/*
+ * The RISCV_MAX_COUNTERS parameter should be specified.
+ */
+
+#ifdef CONFIG_RISCV_BASE_PMU
+#define RISCV_MAX_COUNTERS 2
+#endif
+
+#ifndef RISCV_MAX_COUNTERS
+#error "Please provide a valid RISCV_MAX_COUNTERS for the PMU."
+#endif
+
+/*
+ * These are the indexes of bits in counteren register *minus* 1,
+ * except for cycle. It would be coherent if it can directly mapped
+ * to counteren bit definition, but there is a *time* register at
+ * counteren[1]. Per-cpu structure is scarce resource here.
+ *
+ * According to the spec, an implementation can support counter up to
+ * mhpmcounter31, but many high-end processors has at most 6 general
+ * PMCs, we give the definition to MHPMCOUNTER8 here.
+ */
+#define RISCV_PMU_CYCLE 0
+#define RISCV_PMU_INSTRET 1
+#define RISCV_PMU_MHPMCOUNTER3 2
+#define RISCV_PMU_MHPMCOUNTER4 3
+#define RISCV_PMU_MHPMCOUNTER5 4
+#define RISCV_PMU_MHPMCOUNTER6 5
+#define RISCV_PMU_MHPMCOUNTER7 6
+#define RISCV_PMU_MHPMCOUNTER8 7
+
+#define RISCV_OP_UNSUPP (-EOPNOTSUPP)
+
+struct cpu_hw_events {
+ /* # currently enabled events*/
+ int n_events;
+ /* currently enabled events */
+ struct perf_event *events[RISCV_MAX_COUNTERS];
+ /* vendor-defined PMU data */
+ void *platform;
+};
+
+struct riscv_pmu {
+ struct pmu *pmu;
+
+ /* generic hw/cache events table */
+ const int *hw_events;
+ const int (*cache_events)[PERF_COUNT_HW_CACHE_MAX]
+ [PERF_COUNT_HW_CACHE_OP_MAX]
+ [PERF_COUNT_HW_CACHE_RESULT_MAX];
+ /* method used to map hw/cache events */
+ int (*map_hw_event)(u64 config);
+ int (*map_cache_event)(u64 config);
+
+ /* max generic hw events in map */
+ int max_events;
+ /* number total counters, 2(base) + x(general) */
+ int num_counters;
+ /* the width of the counter */
+ int counter_width;
+
+ /* vendor-defined PMU features */
+ void *platform;
+
+ irqreturn_t (*handle_irq)(int irq_num, void *dev);
+ int irq;
+};
+
+#endif /* _ASM_RISCV_PERF_EVENT_H */
#include <asm/sbi.h>
-#define flush_tlb_all() sbi_remote_sfence_vma(0, 0, -1)
+#define flush_tlb_all() sbi_remote_sfence_vma(NULL, 0, -1)
#define flush_tlb_page(vma, addr) flush_tlb_range(vma, addr, 0)
#define flush_tlb_range(vma, start, end) \
sbi_remote_sfence_vma(mm_cpumask((vma)->vm_mm)->bits, \
})
-extern unsigned long __must_check __copy_user(void __user *to,
+extern unsigned long __must_check __asm_copy_to_user(void __user *to,
+ const void *from, unsigned long n);
+extern unsigned long __must_check __asm_copy_from_user(void *to,
const void __user *from, unsigned long n);
static inline unsigned long
raw_copy_from_user(void *to, const void __user *from, unsigned long n)
{
- return __copy_user(to, from, n);
+ return __asm_copy_to_user(to, from, n);
}
static inline unsigned long
raw_copy_to_user(void __user *to, const void *from, unsigned long n)
{
- return __copy_user(to, from, n);
+ return __asm_copy_from_user(to, from, n);
}
extern long strncpy_from_user(char *dest, const char __user *src, long count);
obj-$(CONFIG_FUNCTION_TRACER) += mcount.o ftrace.o
obj-$(CONFIG_DYNAMIC_FTRACE) += mcount-dyn.o
+obj-$(CONFIG_PERF_EVENTS) += perf_event.o
+
clean:
RESTORE_ABI_STATE
ret
ENDPROC(_mcount)
-EXPORT_SYMBOL(_mcount)
#endif
+EXPORT_SYMBOL(_mcount)
#include <linux/errno.h>
#include <linux/moduleloader.h>
+static int apply_r_riscv_32_rela(struct module *me, u32 *location, Elf_Addr v)
+{
+ if (v != (u32)v) {
+ pr_err("%s: value %016llx out of range for 32-bit field\n",
+ me->name, v);
+ return -EINVAL;
+ }
+ *location = v;
+ return 0;
+}
+
static int apply_r_riscv_64_rela(struct module *me, u32 *location, Elf_Addr v)
{
*(u64 *)location = v;
static int (*reloc_handlers_rela[]) (struct module *me, u32 *location,
Elf_Addr v) = {
+ [R_RISCV_32] = apply_r_riscv_32_rela,
[R_RISCV_64] = apply_r_riscv_64_rela,
[R_RISCV_BRANCH] = apply_r_riscv_branch_rela,
[R_RISCV_JAL] = apply_r_riscv_jal_rela,
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2008 Thomas Gleixner <tglx@linutronix.de>
+ * Copyright (C) 2008-2009 Red Hat, Inc., Ingo Molnar
+ * Copyright (C) 2009 Jaswinder Singh Rajput
+ * Copyright (C) 2009 Advanced Micro Devices, Inc., Robert Richter
+ * Copyright (C) 2008-2009 Red Hat, Inc., Peter Zijlstra
+ * Copyright (C) 2009 Intel Corporation, <markus.t.metzger@intel.com>
+ * Copyright (C) 2009 Google, Inc., Stephane Eranian
+ * Copyright 2014 Tilera Corporation. All Rights Reserved.
+ * Copyright (C) 2018 Andes Technology Corporation
+ *
+ * Perf_events support for RISC-V platforms.
+ *
+ * Since the spec. (as of now, Priv-Spec 1.10) does not provide enough
+ * functionality for perf event to fully work, this file provides
+ * the very basic framework only.
+ *
+ * For platform portings, please check Documentations/riscv/pmu.txt.
+ *
+ * The Copyright line includes x86 and tile ones.
+ */
+
+#include <linux/kprobes.h>
+#include <linux/kernel.h>
+#include <linux/kdebug.h>
+#include <linux/mutex.h>
+#include <linux/bitmap.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/perf_event.h>
+#include <linux/atomic.h>
+#include <linux/of.h>
+#include <asm/perf_event.h>
+
+static const struct riscv_pmu *riscv_pmu __read_mostly;
+static DEFINE_PER_CPU(struct cpu_hw_events, cpu_hw_events);
+
+/*
+ * Hardware & cache maps and their methods
+ */
+
+static const int riscv_hw_event_map[] = {
+ [PERF_COUNT_HW_CPU_CYCLES] = RISCV_PMU_CYCLE,
+ [PERF_COUNT_HW_INSTRUCTIONS] = RISCV_PMU_INSTRET,
+ [PERF_COUNT_HW_CACHE_REFERENCES] = RISCV_OP_UNSUPP,
+ [PERF_COUNT_HW_CACHE_MISSES] = RISCV_OP_UNSUPP,
+ [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = RISCV_OP_UNSUPP,
+ [PERF_COUNT_HW_BRANCH_MISSES] = RISCV_OP_UNSUPP,
+ [PERF_COUNT_HW_BUS_CYCLES] = RISCV_OP_UNSUPP,
+};
+
+#define C(x) PERF_COUNT_HW_CACHE_##x
+static const int riscv_cache_event_map[PERF_COUNT_HW_CACHE_MAX]
+[PERF_COUNT_HW_CACHE_OP_MAX]
+[PERF_COUNT_HW_CACHE_RESULT_MAX] = {
+ [C(L1D)] = {
+ [C(OP_READ)] = {
+ [C(RESULT_ACCESS)] = RISCV_OP_UNSUPP,
+ [C(RESULT_MISS)] = RISCV_OP_UNSUPP,
+ },
+ [C(OP_WRITE)] = {
+ [C(RESULT_ACCESS)] = RISCV_OP_UNSUPP,
+ [C(RESULT_MISS)] = RISCV_OP_UNSUPP,
+ },
+ [C(OP_PREFETCH)] = {
+ [C(RESULT_ACCESS)] = RISCV_OP_UNSUPP,
+ [C(RESULT_MISS)] = RISCV_OP_UNSUPP,
+ },
+ },
+ [C(L1I)] = {
+ [C(OP_READ)] = {
+ [C(RESULT_ACCESS)] = RISCV_OP_UNSUPP,
+ [C(RESULT_MISS)] = RISCV_OP_UNSUPP,
+ },
+ [C(OP_WRITE)] = {
+ [C(RESULT_ACCESS)] = RISCV_OP_UNSUPP,
+ [C(RESULT_MISS)] = RISCV_OP_UNSUPP,
+ },
+ [C(OP_PREFETCH)] = {
+ [C(RESULT_ACCESS)] = RISCV_OP_UNSUPP,
+ [C(RESULT_MISS)] = RISCV_OP_UNSUPP,
+ },
+ },
+ [C(LL)] = {
+ [C(OP_READ)] = {
+ [C(RESULT_ACCESS)] = RISCV_OP_UNSUPP,
+ [C(RESULT_MISS)] = RISCV_OP_UNSUPP,
+ },
+ [C(OP_WRITE)] = {
+ [C(RESULT_ACCESS)] = RISCV_OP_UNSUPP,
+ [C(RESULT_MISS)] = RISCV_OP_UNSUPP,
+ },
+ [C(OP_PREFETCH)] = {
+ [C(RESULT_ACCESS)] = RISCV_OP_UNSUPP,
+ [C(RESULT_MISS)] = RISCV_OP_UNSUPP,
+ },
+ },
+ [C(DTLB)] = {
+ [C(OP_READ)] = {
+ [C(RESULT_ACCESS)] = RISCV_OP_UNSUPP,
+ [C(RESULT_MISS)] = RISCV_OP_UNSUPP,
+ },
+ [C(OP_WRITE)] = {
+ [C(RESULT_ACCESS)] = RISCV_OP_UNSUPP,
+ [C(RESULT_MISS)] = RISCV_OP_UNSUPP,
+ },
+ [C(OP_PREFETCH)] = {
+ [C(RESULT_ACCESS)] = RISCV_OP_UNSUPP,
+ [C(RESULT_MISS)] = RISCV_OP_UNSUPP,
+ },
+ },
+ [C(ITLB)] = {
+ [C(OP_READ)] = {
+ [C(RESULT_ACCESS)] = RISCV_OP_UNSUPP,
+ [C(RESULT_MISS)] = RISCV_OP_UNSUPP,
+ },
+ [C(OP_WRITE)] = {
+ [C(RESULT_ACCESS)] = RISCV_OP_UNSUPP,
+ [C(RESULT_MISS)] = RISCV_OP_UNSUPP,
+ },
+ [C(OP_PREFETCH)] = {
+ [C(RESULT_ACCESS)] = RISCV_OP_UNSUPP,
+ [C(RESULT_MISS)] = RISCV_OP_UNSUPP,
+ },
+ },
+ [C(BPU)] = {
+ [C(OP_READ)] = {
+ [C(RESULT_ACCESS)] = RISCV_OP_UNSUPP,
+ [C(RESULT_MISS)] = RISCV_OP_UNSUPP,
+ },
+ [C(OP_WRITE)] = {
+ [C(RESULT_ACCESS)] = RISCV_OP_UNSUPP,
+ [C(RESULT_MISS)] = RISCV_OP_UNSUPP,
+ },
+ [C(OP_PREFETCH)] = {
+ [C(RESULT_ACCESS)] = RISCV_OP_UNSUPP,
+ [C(RESULT_MISS)] = RISCV_OP_UNSUPP,
+ },
+ },
+};
+
+static int riscv_map_hw_event(u64 config)
+{
+ if (config >= riscv_pmu->max_events)
+ return -EINVAL;
+
+ return riscv_pmu->hw_events[config];
+}
+
+int riscv_map_cache_decode(u64 config, unsigned int *type,
+ unsigned int *op, unsigned int *result)
+{
+ return -ENOENT;
+}
+
+static int riscv_map_cache_event(u64 config)
+{
+ unsigned int type, op, result;
+ int err = -ENOENT;
+ int code;
+
+ err = riscv_map_cache_decode(config, &type, &op, &result);
+ if (!riscv_pmu->cache_events || err)
+ return err;
+
+ if (type >= PERF_COUNT_HW_CACHE_MAX ||
+ op >= PERF_COUNT_HW_CACHE_OP_MAX ||
+ result >= PERF_COUNT_HW_CACHE_RESULT_MAX)
+ return -EINVAL;
+
+ code = (*riscv_pmu->cache_events)[type][op][result];
+ if (code == RISCV_OP_UNSUPP)
+ return -EINVAL;
+
+ return code;
+}
+
+/*
+ * Low-level functions: reading/writing counters
+ */
+
+static inline u64 read_counter(int idx)
+{
+ u64 val = 0;
+
+ switch (idx) {
+ case RISCV_PMU_CYCLE:
+ val = csr_read(cycle);
+ break;
+ case RISCV_PMU_INSTRET:
+ val = csr_read(instret);
+ break;
+ default:
+ WARN_ON_ONCE(idx < 0 || idx > RISCV_MAX_COUNTERS);
+ return -EINVAL;
+ }
+
+ return val;
+}
+
+static inline void write_counter(int idx, u64 value)
+{
+ /* currently not supported */
+ WARN_ON_ONCE(1);
+}
+
+/*
+ * pmu->read: read and update the counter
+ *
+ * Other architectures' implementation often have a xxx_perf_event_update
+ * routine, which can return counter values when called in the IRQ, but
+ * return void when being called by the pmu->read method.
+ */
+static void riscv_pmu_read(struct perf_event *event)
+{
+ struct hw_perf_event *hwc = &event->hw;
+ u64 prev_raw_count, new_raw_count;
+ u64 oldval;
+ int idx = hwc->idx;
+ u64 delta;
+
+ do {
+ prev_raw_count = local64_read(&hwc->prev_count);
+ new_raw_count = read_counter(idx);
+
+ oldval = local64_cmpxchg(&hwc->prev_count, prev_raw_count,
+ new_raw_count);
+ } while (oldval != prev_raw_count);
+
+ /*
+ * delta is the value to update the counter we maintain in the kernel.
+ */
+ delta = (new_raw_count - prev_raw_count) &
+ ((1ULL << riscv_pmu->counter_width) - 1);
+ local64_add(delta, &event->count);
+ /*
+ * Something like local64_sub(delta, &hwc->period_left) here is
+ * needed if there is an interrupt for perf.
+ */
+}
+
+/*
+ * State transition functions:
+ *
+ * stop()/start() & add()/del()
+ */
+
+/*
+ * pmu->stop: stop the counter
+ */
+static void riscv_pmu_stop(struct perf_event *event, int flags)
+{
+ struct hw_perf_event *hwc = &event->hw;
+
+ WARN_ON_ONCE(hwc->state & PERF_HES_STOPPED);
+ hwc->state |= PERF_HES_STOPPED;
+
+ if ((flags & PERF_EF_UPDATE) && !(hwc->state & PERF_HES_UPTODATE)) {
+ riscv_pmu->pmu->read(event);
+ hwc->state |= PERF_HES_UPTODATE;
+ }
+}
+
+/*
+ * pmu->start: start the event.
+ */
+static void riscv_pmu_start(struct perf_event *event, int flags)
+{
+ struct hw_perf_event *hwc = &event->hw;
+
+ if (WARN_ON_ONCE(!(event->hw.state & PERF_HES_STOPPED)))
+ return;
+
+ if (flags & PERF_EF_RELOAD) {
+ WARN_ON_ONCE(!(event->hw.state & PERF_HES_UPTODATE));
+
+ /*
+ * Set the counter to the period to the next interrupt here,
+ * if you have any.
+ */
+ }
+
+ hwc->state = 0;
+ perf_event_update_userpage(event);
+
+ /*
+ * Since we cannot write to counters, this serves as an initialization
+ * to the delta-mechanism in pmu->read(); otherwise, the delta would be
+ * wrong when pmu->read is called for the first time.
+ */
+ local64_set(&hwc->prev_count, read_counter(hwc->idx));
+}
+
+/*
+ * pmu->add: add the event to PMU.
+ */
+static int riscv_pmu_add(struct perf_event *event, int flags)
+{
+ struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
+ struct hw_perf_event *hwc = &event->hw;
+
+ if (cpuc->n_events == riscv_pmu->num_counters)
+ return -ENOSPC;
+
+ /*
+ * We don't have general conunters, so no binding-event-to-counter
+ * process here.
+ *
+ * Indexing using hwc->config generally not works, since config may
+ * contain extra information, but here the only info we have in
+ * hwc->config is the event index.
+ */
+ hwc->idx = hwc->config;
+ cpuc->events[hwc->idx] = event;
+ cpuc->n_events++;
+
+ hwc->state = PERF_HES_UPTODATE | PERF_HES_STOPPED;
+
+ if (flags & PERF_EF_START)
+ riscv_pmu->pmu->start(event, PERF_EF_RELOAD);
+
+ return 0;
+}
+
+/*
+ * pmu->del: delete the event from PMU.
+ */
+static void riscv_pmu_del(struct perf_event *event, int flags)
+{
+ struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
+ struct hw_perf_event *hwc = &event->hw;
+
+ cpuc->events[hwc->idx] = NULL;
+ cpuc->n_events--;
+ riscv_pmu->pmu->stop(event, PERF_EF_UPDATE);
+ perf_event_update_userpage(event);
+}
+
+/*
+ * Interrupt: a skeletion for reference.
+ */
+
+static DEFINE_MUTEX(pmc_reserve_mutex);
+
+irqreturn_t riscv_base_pmu_handle_irq(int irq_num, void *dev)
+{
+ return IRQ_NONE;
+}
+
+static int reserve_pmc_hardware(void)
+{
+ int err = 0;
+
+ mutex_lock(&pmc_reserve_mutex);
+ if (riscv_pmu->irq >= 0 && riscv_pmu->handle_irq) {
+ err = request_irq(riscv_pmu->irq, riscv_pmu->handle_irq,
+ IRQF_PERCPU, "riscv-base-perf", NULL);
+ }
+ mutex_unlock(&pmc_reserve_mutex);
+
+ return err;
+}
+
+void release_pmc_hardware(void)
+{
+ mutex_lock(&pmc_reserve_mutex);
+ if (riscv_pmu->irq >= 0)
+ free_irq(riscv_pmu->irq, NULL);
+ mutex_unlock(&pmc_reserve_mutex);
+}
+
+/*
+ * Event Initialization/Finalization
+ */
+
+static atomic_t riscv_active_events = ATOMIC_INIT(0);
+
+static void riscv_event_destroy(struct perf_event *event)
+{
+ if (atomic_dec_return(&riscv_active_events) == 0)
+ release_pmc_hardware();
+}
+
+static int riscv_event_init(struct perf_event *event)
+{
+ struct perf_event_attr *attr = &event->attr;
+ struct hw_perf_event *hwc = &event->hw;
+ int err;
+ int code;
+
+ if (atomic_inc_return(&riscv_active_events) == 1) {
+ err = reserve_pmc_hardware();
+
+ if (err) {
+ pr_warn("PMC hardware not available\n");
+ atomic_dec(&riscv_active_events);
+ return -EBUSY;
+ }
+ }
+
+ switch (event->attr.type) {
+ case PERF_TYPE_HARDWARE:
+ code = riscv_pmu->map_hw_event(attr->config);
+ break;
+ case PERF_TYPE_HW_CACHE:
+ code = riscv_pmu->map_cache_event(attr->config);
+ break;
+ case PERF_TYPE_RAW:
+ return -EOPNOTSUPP;
+ default:
+ return -ENOENT;
+ }
+
+ event->destroy = riscv_event_destroy;
+ if (code < 0) {
+ event->destroy(event);
+ return code;
+ }
+
+ /*
+ * idx is set to -1 because the index of a general event should not be
+ * decided until binding to some counter in pmu->add().
+ *
+ * But since we don't have such support, later in pmu->add(), we just
+ * use hwc->config as the index instead.
+ */
+ hwc->config = code;
+ hwc->idx = -1;
+
+ return 0;
+}
+
+/*
+ * Initialization
+ */
+
+static struct pmu min_pmu = {
+ .name = "riscv-base",
+ .event_init = riscv_event_init,
+ .add = riscv_pmu_add,
+ .del = riscv_pmu_del,
+ .start = riscv_pmu_start,
+ .stop = riscv_pmu_stop,
+ .read = riscv_pmu_read,
+};
+
+static const struct riscv_pmu riscv_base_pmu = {
+ .pmu = &min_pmu,
+ .max_events = ARRAY_SIZE(riscv_hw_event_map),
+ .map_hw_event = riscv_map_hw_event,
+ .hw_events = riscv_hw_event_map,
+ .map_cache_event = riscv_map_cache_event,
+ .cache_events = &riscv_cache_event_map,
+ .counter_width = 63,
+ .num_counters = RISCV_BASE_COUNTERS + 0,
+ .handle_irq = &riscv_base_pmu_handle_irq,
+
+ /* This means this PMU has no IRQ. */
+ .irq = -1,
+};
+
+static const struct of_device_id riscv_pmu_of_ids[] = {
+ {.compatible = "riscv,base-pmu", .data = &riscv_base_pmu},
+ { /* sentinel value */ }
+};
+
+int __init init_hw_perf_events(void)
+{
+ struct device_node *node = of_find_node_by_type(NULL, "pmu");
+ const struct of_device_id *of_id;
+
+ riscv_pmu = &riscv_base_pmu;
+
+ if (node) {
+ of_id = of_match_node(riscv_pmu_of_ids, node);
+
+ if (of_id)
+ riscv_pmu = of_id->data;
+ }
+
+ perf_pmu_register(riscv_pmu->pmu, "cpu", PERF_TYPE_RAW);
+ return 0;
+}
+arch_initcall(init_hw_perf_events);
* Assembly functions that may be used (directly or indirectly) by modules
*/
EXPORT_SYMBOL(__clear_user);
-EXPORT_SYMBOL(__copy_user);
+EXPORT_SYMBOL(__asm_copy_to_user);
+EXPORT_SYMBOL(__asm_copy_from_user);
EXPORT_SYMBOL(memset);
EXPORT_SYMBOL(memcpy);
if (pc < PAGE_OFFSET)
return 0;
- if (probe_kernel_address((bug_insn_t __user *)pc, insn))
+ if (probe_kernel_address((bug_insn_t *)pc, insn))
return 0;
return (insn == __BUG_INSN);
}
.previous
.endm
-ENTRY(__copy_user)
+ENTRY(__asm_copy_to_user)
+ENTRY(__asm_copy_from_user)
/* Enable access to user memory */
li t6, SR_SUM
addi a0, a0, 1
bltu a1, a3, 5b
j 3b
-ENDPROC(__copy_user)
+ENDPROC(__asm_copy_to_user)
+ENDPROC(__asm_copy_from_user)
ENTRY(__clear_user)
bgeu t0, t1, 2f
bltu a0, t0, 4f
1:
- fixup REG_S, zero, (a0), 10f
+ fixup REG_S, zero, (a0), 11f
addi a0, a0, SZREG
bltu a0, t1, 1b
2:
li a0, 0
ret
4: /* Edge case: unalignment */
- fixup sb, zero, (a0), 10f
+ fixup sb, zero, (a0), 11f
addi a0, a0, 1
bltu a0, t0, 4b
j 1b
5: /* Edge case: remainder */
- fixup sb, zero, (a0), 10f
+ fixup sb, zero, (a0), 11f
addi a0, a0, 1
bltu a0, a3, 5b
j 3b
.section .fixup,"ax"
.balign 4
+ /* Fixup code for __copy_user(10) and __clear_user(11) */
10:
/* Disable access to user memory */
csrs sstatus, t6
- sub a0, a3, a0
+ mv a0, a2
+ ret
+11:
+ csrs sstatus, t6
+ mv a0, a1
ret
.previous
struct css_general_char {
u64 : 12;
- u32 dynio : 1; /* bit 12 */
- u32 : 4;
- u32 eadm : 1; /* bit 17 */
- u32 : 23;
- u32 aif : 1; /* bit 41 */
- u32 : 3;
- u32 mcss : 1; /* bit 45 */
- u32 fcs : 1; /* bit 46 */
- u32 : 1;
- u32 ext_mb : 1; /* bit 48 */
- u32 : 7;
- u32 aif_tdd : 1; /* bit 56 */
- u32 : 1;
- u32 qebsm : 1; /* bit 58 */
- u32 : 2;
- u32 aiv : 1; /* bit 61 */
- u32 : 5;
- u32 aif_osa : 1; /* bit 67 */
- u32 : 12;
- u32 eadm_rf : 1; /* bit 80 */
- u32 : 1;
- u32 cib : 1; /* bit 82 */
- u32 : 5;
- u32 fcx : 1; /* bit 88 */
- u32 : 19;
- u32 alt_ssi : 1; /* bit 108 */
- u32 : 1;
- u32 narf : 1; /* bit 110 */
- u32 : 12;
- u32 util_str : 1;/* bit 123 */
+ u64 dynio : 1; /* bit 12 */
+ u64 : 4;
+ u64 eadm : 1; /* bit 17 */
+ u64 : 23;
+ u64 aif : 1; /* bit 41 */
+ u64 : 3;
+ u64 mcss : 1; /* bit 45 */
+ u64 fcs : 1; /* bit 46 */
+ u64 : 1;
+ u64 ext_mb : 1; /* bit 48 */
+ u64 : 7;
+ u64 aif_tdd : 1; /* bit 56 */
+ u64 : 1;
+ u64 qebsm : 1; /* bit 58 */
+ u64 : 2;
+ u64 aiv : 1; /* bit 61 */
+ u64 : 2;
+
+ u64 : 3;
+ u64 aif_osa : 1; /* bit 67 */
+ u64 : 12;
+ u64 eadm_rf : 1; /* bit 80 */
+ u64 : 1;
+ u64 cib : 1; /* bit 82 */
+ u64 : 5;
+ u64 fcx : 1; /* bit 88 */
+ u64 : 19;
+ u64 alt_ssi : 1; /* bit 108 */
+ u64 : 1;
+ u64 narf : 1; /* bit 110 */
+ u64 : 12;
+ u64 util_str : 1;/* bit 123 */
} __packed;
extern struct css_general_char css_general_characteristics;
select PERF_EVENTS
select ARCH_HIBERNATION_POSSIBLE if MMU
select SPARSE_IRQ
- select HAVE_CC_STACKPROTECTOR
+ select HAVE_STACKPROTECTOR
config SUPERH64
def_bool "$(ARCH)" = "sh64"
People using multiprocessor machines who say Y here should also say
Y to "Enhanced Real Time Clock Support", below.
- See also <file:Documentation/nmi_watchdog.txt> and the SMP-HOWTO
+ See also <file:Documentation/lockup-watchdogs.txt> and the SMP-HOWTO
available at <http://www.tldp.org/docs.html#howto>.
If you don't know what to do here, say N.
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/i2c.h>
-#include <linux/i2c-pca-platform.h>
+#include <linux/platform_data/i2c-pca-platform.h>
#include <linux/i2c-algo-pca.h>
#include <linux/usb/r8a66597.h>
#include <linux/sh_intc.h>
struct kmem_cache *task_xstate_cachep = NULL;
unsigned int xstate_size;
-#ifdef CONFIG_CC_STACKPROTECTOR
+#ifdef CONFIG_STACKPROTECTOR
unsigned long __stack_chk_guard __read_mostly;
EXPORT_SYMBOL(__stack_chk_guard);
#endif
{
struct thread_struct *next_t = &next->thread;
-#if defined(CONFIG_CC_STACKPROTECTOR) && !defined(CONFIG_SMP)
+#if defined(CONFIG_STACKPROTECTOR) && !defined(CONFIG_SMP)
__stack_chk_guard = next->stack_canary;
#endif
Y to "Enhanced Real Time Clock Support", below. The "Advanced Power
Management" code will be disabled if you say Y here.
- See also <file:Documentation/nmi_watchdog.txt> and the SMP-HOWTO
+ See also <file:Documentation/lockup-watchdogs.txt> and the SMP-HOWTO
available at <http://www.tldp.org/docs.html#howto>.
If you don't know what to do here, say N.
(top_of_ram - total_ram) >> 20);
init_node_masks_nonnuma();
- memblock_set_node(0, (phys_addr_t)ULLONG_MAX, &memblock.memory, 0);
+ memblock_set_node(0, PHYS_ADDR_MAX, &memblock.memory, 0);
allocate_node_data(0);
node_set_online(0);
}
On UML, this is accomplished by sending a "sysrq" command with
mconsole, followed by the letter for the requested command.
- The keys are documented in <file:Documentation/sysrq.txt>. Don't say Y
+ The keys are documented in <file:Documentation/admin-guide/sysrq.rst>. Don't say Y
unless you really know what this hack does.
config KERNEL_STACK_ORDER
if (strncmp(transport, TRANS_TAP, TRANS_TAP_LEN) == 0)
return (vec_rx | VECTOR_BPF);
if (strncmp(transport, TRANS_RAW, TRANS_RAW_LEN) == 0)
- return (vec_rx | vec_tx);
+ return (vec_rx | vec_tx | VECTOR_QDISC_BYPASS);
return (vec_rx | vec_tx);
}
result = kmalloc(sizeof(struct vector_queue), GFP_KERNEL);
if (result == NULL)
- goto out_fail;
+ return NULL;
result->max_depth = max_size;
result->dev = vp->dev;
result->mmsg_vector = kmalloc(
(sizeof(struct mmsghdr) * max_size), GFP_KERNEL);
+ if (result->mmsg_vector == NULL)
+ goto out_mmsg_fail;
result->skbuff_vector = kmalloc(
(sizeof(void *) * max_size), GFP_KERNEL);
- if (result->mmsg_vector == NULL || result->skbuff_vector == NULL)
- goto out_fail;
+ if (result->skbuff_vector == NULL)
+ goto out_skb_fail;
+
+ /* further failures can be handled safely by destroy_queue*/
mmsg_vector = result->mmsg_vector;
for (i = 0; i < max_size; i++) {
result->head = 0;
result->tail = 0;
return result;
+out_skb_fail:
+ kfree(result->mmsg_vector);
+out_mmsg_fail:
+ kfree(result);
+ return NULL;
out_fail:
destroy_queue(result);
return NULL;
if ((vp->options & VECTOR_QDISC_BYPASS) != 0) {
if (!uml_raw_enable_qdisc_bypass(vp->fds->rx_fd))
- vp->options = vp->options | VECTOR_BPF;
+ vp->options |= VECTOR_BPF;
}
-
if ((vp->options & VECTOR_BPF) != 0)
vp->bpf = uml_vector_default_bpf(vp->fds->rx_fd, dev->dev_addr);
CON_INITCALL
}
- .uml.initcall.init : {
- __uml_initcall_start = .;
- *(.uml.initcall.init)
- __uml_initcall_end = .;
- }
-
SECURITY_INIT
.exitcall : {
int (*setup_func)(char *, int *);
};
-extern initcall_t __uml_initcall_start, __uml_initcall_end;
extern initcall_t __uml_postsetup_start, __uml_postsetup_end;
extern const char *__uml_help_start, *__uml_help_end;
#endif
-#define __uml_initcall(fn) \
- static initcall_t __uml_initcall_##fn __uml_init_call = fn
-
#define __uml_exitcall(fn) \
static exitcall_t __uml_exitcall_##fn __uml_exit_call = fn
*/
#define __uml_init_setup __used __section(.uml.setup.init)
#define __uml_setup_help __used __section(.uml.help.init)
-#define __uml_init_call __used __section(.uml.initcall.init)
#define __uml_postsetup_call __used __section(.uml.postsetup.init)
#define __uml_exit_call __used __section(.uml.exitcall.exit)
}
}
-static __init void do_uml_initcalls(void)
-{
- initcall_t *call;
-
- call = &__uml_initcall_start;
- while (call < &__uml_initcall_end) {
- (*call)();
- call++;
- }
-}
-
static void last_ditch_exit(int sig)
{
uml_cleanup();
scan_elf_aux(envp);
#endif
- do_uml_initcalls();
change_sig(SIGPIPE, 0);
ret = linux_main(argc, argv);
* Start addresses are inclusive and end addresses are exclusive;
* start addresses should be rounded down, end addresses up.
*
- * See Documentation/cachetlb.txt for more information.
+ * See Documentation/core-api/cachetlb.rst for more information.
* Please note that the implementation of these, and the required
* effects are cache-type (VIVT/VIPT/PIPT) specific.
*
select HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD if X86_64
select HAVE_ARCH_VMAP_STACK if X86_64
select HAVE_ARCH_WITHIN_STACK_FRAMES
- select HAVE_CC_STACKPROTECTOR if CC_HAS_SANE_STACKPROTECTOR
select HAVE_CMPXCHG_DOUBLE
select HAVE_CMPXCHG_LOCAL
select HAVE_CONTEXT_TRACKING if X86_64
select HAVE_RCU_TABLE_FREE
select HAVE_REGS_AND_STACK_ACCESS_API
select HAVE_RELIABLE_STACKTRACE if X86_64 && UNWINDER_FRAME_POINTER && STACK_VALIDATION
+ select HAVE_STACKPROTECTOR if CC_HAS_SANE_STACKPROTECTOR
select HAVE_STACK_VALIDATION if X86_64
select HAVE_RSEQ
select HAVE_SYSCALL_TRACEPOINTS
config X86_32_LAZY_GS
def_bool y
- depends on X86_32 && CC_STACKPROTECTOR_NONE
+ depends on X86_32 && !STACKPROTECTOR
config ARCH_SUPPORTS_UPROBES
def_bool y
movl %esp, TASK_threadsp(%eax)
movl TASK_threadsp(%edx), %esp
-#ifdef CONFIG_CC_STACKPROTECTOR
+#ifdef CONFIG_STACKPROTECTOR
movl TASK_stack_canary(%edx), %ebx
movl %ebx, PER_CPU_VAR(stack_canary)+stack_canary_offset
#endif
movq %rsp, TASK_threadsp(%rdi)
movq TASK_threadsp(%rsi), %rsp
-#ifdef CONFIG_CC_STACKPROTECTOR
+#ifdef CONFIG_STACKPROTECTOR
movq TASK_stack_canary(%rsi), %rbx
movq %rbx, PER_CPU_VAR(irq_stack_union)+stack_canary_offset
#endif
/*
* Handle seccomp. regs->ip must be the original value.
- * See seccomp_send_sigsys and Documentation/prctl/seccomp_filter.txt.
+ * See seccomp_send_sigsys and Documentation/userspace-api/seccomp_filter.rst.
*
* We could optimize the seccomp disabled case, but performance
* here doesn't matter.
void save_fsgs_for_kvm(void);
#endif
#else /* X86_64 */
-#ifdef CONFIG_CC_STACKPROTECTOR
+#ifdef CONFIG_STACKPROTECTOR
/*
* Make sure stack canary segment base is cached-aligned:
* "For Intel Atom processors, avoid non zero segment base address
# define __KERNEL_PERCPU 0
#endif
-#ifdef CONFIG_CC_STACKPROTECTOR
+#ifdef CONFIG_STACKPROTECTOR
# define __KERNEL_STACK_CANARY (GDT_ENTRY_STACK_CANARY*8)
#else
# define __KERNEL_STACK_CANARY 0
#ifndef _ASM_STACKPROTECTOR_H
#define _ASM_STACKPROTECTOR_H 1
-#ifdef CONFIG_CC_STACKPROTECTOR
+#ifdef CONFIG_STACKPROTECTOR
#include <asm/tsc.h>
#include <asm/processor.h>
#endif
}
-#else /* CC_STACKPROTECTOR */
+#else /* STACKPROTECTOR */
#define GDT_STACK_CANARY_INIT
#endif
}
-#endif /* CC_STACKPROTECTOR */
+#endif /* STACKPROTECTOR */
#endif /* _ASM_STACKPROTECTOR_H */
void common(void) {
BLANK();
OFFSET(TASK_threadsp, task_struct, thread.sp);
-#ifdef CONFIG_CC_STACKPROTECTOR
+#ifdef CONFIG_STACKPROTECTOR
OFFSET(TASK_stack_canary, task_struct, stack_canary);
#endif
DEFINE(TSS_sysenter_sp0, offsetof(struct cpu_entry_area, tss.x86_tss.sp0) -
offsetofend(struct cpu_entry_area, entry_stack_page.stack));
-#ifdef CONFIG_CC_STACKPROTECTOR
+#ifdef CONFIG_STACKPROTECTOR
BLANK();
OFFSET(stack_canary_offset, stack_canary, canary);
#endif
OFFSET(TSS_sp1, tss_struct, x86_tss.sp1);
BLANK();
-#ifdef CONFIG_CC_STACKPROTECTOR
+#ifdef CONFIG_STACKPROTECTOR
DEFINE(stack_canary_offset, offsetof(union irq_stack_union, stack_canary));
BLANK();
#endif
(unsigned long)&init_thread_union + THREAD_SIZE;
EXPORT_PER_CPU_SYMBOL(cpu_current_top_of_stack);
-#ifdef CONFIG_CC_STACKPROTECTOR
+#ifdef CONFIG_STACKPROTECTOR
DEFINE_PER_CPU_ALIGNED(struct stack_canary, stack_canary);
#endif
*/
__INIT
setup_once:
-#ifdef CONFIG_CC_STACKPROTECTOR
+#ifdef CONFIG_STACKPROTECTOR
/*
* Configure the stack canary. The linker can't handle this by
* relocation. Manually set base address in stack canary
goto out_vmcs;
memset(loaded_vmcs->msr_bitmap, 0xff, PAGE_SIZE);
-#if IS_ENABLED(CONFIG_HYPERV)
- if (static_branch_unlikely(&enable_evmcs) &&
+ if (IS_ENABLED(CONFIG_HYPERV) &&
+ static_branch_unlikely(&enable_evmcs) &&
(ms_hyperv.nested_features & HV_X64_NESTED_MSR_BITMAP)) {
struct hv_enlightened_vmcs *evmcs =
(struct hv_enlightened_vmcs *)loaded_vmcs->vmcs;
evmcs->hv_enlightenments_control.msr_bitmap = 1;
}
-#endif
-
}
return 0;
/*
* Make sure the user can only configure tsc_khz values that
* fit into a signed integer.
- * A min value is not calculated needed because it will always
+ * A min value is not calculated because it will always
* be 1 on all machines.
*/
u64 max = min(0x7fffffffULL,
*/
int devmem_is_allowed(unsigned long pagenr)
{
- if (page_is_ram(pagenr)) {
+ if (region_intersects(PFN_PHYS(pagenr), PAGE_SIZE,
+ IORESOURCE_SYSTEM_RAM, IORES_DESC_NONE)
+ != REGION_DISJOINT) {
/*
* For disallowed memory regions in the low 1MB range,
* request that the page be shown as all zeros.
high_memory = (void *) __va(max_low_pfn * PAGE_SIZE - 1) + 1;
#endif
- memblock_set_node(0, (phys_addr_t)ULLONG_MAX, &memblock.memory, 0);
+ memblock_set_node(0, PHYS_ADDR_MAX, &memblock.memory, 0);
sparse_memory_present_with_active_regions(0);
#ifdef CONFIG_FLATMEM
#ifndef CONFIG_NUMA
void __init initmem_init(void)
{
- memblock_set_node(0, (phys_addr_t)ULLONG_MAX, &memblock.memory, 0);
+ memblock_set_node(0, PHYS_ADDR_MAX, &memblock.memory, 0);
}
#endif
select GENERIC_SCHED_CLOCK
select GENERIC_STRNCPY_FROM_USER if KASAN
select HAVE_ARCH_KASAN if MMU
- select HAVE_CC_STACKPROTECTOR
select HAVE_DEBUG_KMEMLEAK
select HAVE_DMA_CONTIGUOUS
select HAVE_EXIT_THREAD
select HAVE_MEMBLOCK
select HAVE_OPROFILE
select HAVE_PERF_EVENTS
+ select HAVE_STACKPROTECTOR
select IRQ_DOMAIN
select MODULES_USE_ELF_RELA
select NO_BOOTMEM
*
* Pages can get remapped. Because this might change the 'color' of that page,
* we have to flush the cache before the PTE is changed.
- * (see also Documentation/cachetlb.txt)
+ * (see also Documentation/core-api/cachetlb.rst)
*/
#if defined(CONFIG_MMU) && \
__invalidate_icache_range(start,(end) - (start)); \
} while (0)
-/* This is not required, see Documentation/cachetlb.txt */
+/* This is not required, see Documentation/core-api/cachetlb.rst */
#define flush_icache_page(vma,page) do { } while (0)
#define flush_dcache_mmap_lock(mapping) do { } while (0)
DEFINE(TASK_PID, offsetof (struct task_struct, pid));
DEFINE(TASK_THREAD, offsetof (struct task_struct, thread));
DEFINE(TASK_THREAD_INFO, offsetof (struct task_struct, stack));
-#ifdef CONFIG_CC_STACKPROTECTOR
+#ifdef CONFIG_STACKPROTECTOR
DEFINE(TASK_STACK_CANARY, offsetof(struct task_struct, stack_canary));
#endif
DEFINE(TASK_STRUCT_SIZE, sizeof (struct task_struct));
s32i a1, a2, THREAD_SP # save stack pointer
#endif
-#if defined(CONFIG_CC_STACKPROTECTOR) && !defined(CONFIG_SMP)
+#if defined(CONFIG_STACKPROTECTOR) && !defined(CONFIG_SMP)
movi a6, __stack_chk_guard
l32i a8, a3, TASK_STACK_CANARY
s32i a8, a6, 0
EXPORT_SYMBOL(pm_power_off);
-#ifdef CONFIG_CC_STACKPROTECTOR
+#ifdef CONFIG_STACKPROTECTOR
#include <linux/stackprotector.h>
unsigned long __stack_chk_guard __read_mostly;
EXPORT_SYMBOL(__stack_chk_guard);
one needs to mount and use blkio cgroup controller for creating
cgroups and specifying per device IO rate policies.
- See Documentation/cgroups/blkio-controller.txt for more information.
+ See Documentation/cgroup-v1/blkio-controller.txt for more information.
config BLK_DEV_THROTTLING_LOW
bool "Block throttling .low limit interface support (EXPERIMENTAL)"
}
EXPORT_SYMBOL(blk_mq_tagset_busy_iter);
-int blk_mq_tagset_iter(struct blk_mq_tag_set *set, void *data,
- int (fn)(void *, struct request *))
-{
- int i, j, ret = 0;
-
- if (WARN_ON_ONCE(!fn))
- goto out;
-
- for (i = 0; i < set->nr_hw_queues; i++) {
- struct blk_mq_tags *tags = set->tags[i];
-
- if (!tags)
- continue;
-
- for (j = 0; j < tags->nr_tags; j++) {
- if (!tags->static_rqs[j])
- continue;
-
- ret = fn(data, tags->static_rqs[j]);
- if (ret)
- goto out;
- }
- }
-
-out:
- return ret;
-}
-EXPORT_SYMBOL_GPL(blk_mq_tagset_iter);
-
void blk_mq_queue_tag_busy_iter(struct request_queue *q, busy_iter_fn *fn,
void *priv)
{
if (blk_mq_request_started(rq)) {
WRITE_ONCE(rq->state, MQ_RQ_IDLE);
+ rq->rq_flags &= ~RQF_TIMED_OUT;
if (q->dma_drain_size && blk_rq_bytes(rq))
rq->nr_phys_segments--;
}
static void blk_mq_rq_timed_out(struct request *req, bool reserved)
{
+ req->rq_flags |= RQF_TIMED_OUT;
if (req->q->mq_ops->timeout) {
enum blk_eh_timer_return ret;
WARN_ON_ONCE(ret != BLK_EH_RESET_TIMER);
}
+ req->rq_flags &= ~RQF_TIMED_OUT;
blk_add_timer(req);
}
if (blk_mq_rq_state(rq) != MQ_RQ_IN_FLIGHT)
return false;
+ if (rq->rq_flags & RQF_TIMED_OUT)
+ return false;
deadline = blk_rq_deadline(rq);
if (time_after_eq(jiffies, deadline))
mutex_lock(&set->tag_list_lock);
list_del_rcu(&q->tag_set_list);
- INIT_LIST_HEAD(&q->tag_set_list);
if (list_is_singular(&set->tag_list)) {
/* just transitioned to unshared */
set->flags &= ~BLK_MQ_F_TAG_SHARED;
blk_mq_update_tag_set_depth(set, false);
}
mutex_unlock(&set->tag_list_lock);
-
synchronize_rcu();
+ INIT_LIST_HEAD(&q->tag_set_list);
}
static void blk_mq_add_queue_tag_set(struct blk_mq_tag_set *set,
*/
q->queue_tags = tags;
queue_flag_set_unlocked(QUEUE_FLAG_QUEUED, q);
- INIT_LIST_HEAD(&q->tag_busy_list);
return 0;
}
EXPORT_SYMBOL(blk_queue_init_tags);
rq->tag = tag;
bqt->tag_index[tag] = rq;
blk_start_request(rq);
- list_add(&rq->queuelist, &q->tag_busy_list);
return 0;
}
EXPORT_SYMBOL(blk_queue_start_tag);
-
-/**
- * blk_queue_invalidate_tags - invalidate all pending tags
- * @q: the request queue for the device
- *
- * Description:
- * Hardware conditions may dictate a need to stop all pending requests.
- * In this case, we will safely clear the block side of the tag queue and
- * readd all requests to the request queue in the right order.
- **/
-void blk_queue_invalidate_tags(struct request_queue *q)
-{
- struct list_head *tmp, *n;
-
- lockdep_assert_held(q->queue_lock);
-
- list_for_each_safe(tmp, n, &q->tag_busy_list)
- blk_requeue_request(q, list_entry_rq(tmp));
-}
-EXPORT_SYMBOL(blk_queue_invalidate_tags);
struct bsg_device *bd;
unsigned char buf[32];
+ lockdep_assert_held(&bsg_mutex);
+
if (!blk_get_queue(rq))
return ERR_PTR(-ENXIO);
bsg_set_block(bd, file);
atomic_set(&bd->ref_count, 1);
- mutex_lock(&bsg_mutex);
hlist_add_head(&bd->dev_list, bsg_dev_idx_hash(iminor(inode)));
strncpy(bd->name, dev_name(rq->bsg_dev.class_dev), sizeof(bd->name) - 1);
bsg_dbg(bd, "bound to <%s>, max queue %d\n",
format_dev_t(buf, inode->i_rdev), bd->max_queue);
- mutex_unlock(&bsg_mutex);
return bd;
}
{
struct bsg_device *bd;
- mutex_lock(&bsg_mutex);
+ lockdep_assert_held(&bsg_mutex);
hlist_for_each_entry(bd, bsg_dev_idx_hash(minor), dev_list) {
if (bd->queue == q) {
}
bd = NULL;
found:
- mutex_unlock(&bsg_mutex);
return bd;
}
*/
mutex_lock(&bsg_mutex);
bcd = idr_find(&bsg_minor_idr, iminor(inode));
- mutex_unlock(&bsg_mutex);
- if (!bcd)
- return ERR_PTR(-ENODEV);
+ if (!bcd) {
+ bd = ERR_PTR(-ENODEV);
+ goto out_unlock;
+ }
bd = __bsg_get_device(iminor(inode), bcd->queue);
- if (bd)
- return bd;
-
- bd = bsg_add_device(inode, bcd->queue, file);
+ if (!bd)
+ bd = bsg_add_device(inode, bcd->queue, file);
+out_unlock:
+ mutex_unlock(&bsg_mutex);
return bd;
}
If this option is unchanged from its default "certs/signing_key.pem",
then the kernel will automatically generate the private key and
- certificate as described in Documentation/module-signing.txt
+ certificate as described in Documentation/admin-guide/module-signing.rst
config SYSTEM_TRUSTED_KEYRING
bool "Provide system-wide ring of trusted keys"
/* Asymmetric public-key cryptography key type
*
- * See Documentation/security/asymmetric-keys.txt
+ * See Documentation/crypto/asymmetric-keys.txt
*
* Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
/* Signature verification with an asymmetric key
*
- * See Documentation/security/asymmetric-keys.txt
+ * See Documentation/crypto/asymmetric-keys.txt
*
* Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
static const struct lpss_device_desc byt_pwm_dev_desc = {
.flags = LPSS_SAVE_CTX,
+ .prv_offset = 0x800,
.setup = byt_pwm_setup,
};
static const struct lpss_device_desc bsw_pwm_dev_desc = {
.flags = LPSS_SAVE_CTX | LPSS_NO_D3_DELAY,
+ .prv_offset = 0x800,
.setup = bsw_pwm_setup,
};
static int rbd_obj_issue_copyup(struct rbd_obj_request *obj_req, u32 bytes)
{
unsigned int num_osd_ops = obj_req->osd_req->r_num_ops;
+ int ret;
dout("%s obj_req %p bytes %u\n", __func__, obj_req, bytes);
rbd_assert(obj_req->osd_req->r_ops[0].op == CEPH_OSD_OP_STAT);
if (!obj_req->osd_req)
return -ENOMEM;
+ ret = osd_req_op_cls_init(obj_req->osd_req, 0, CEPH_OSD_OP_CALL, "rbd",
+ "copyup");
+ if (ret)
+ return ret;
+
/*
* Only send non-zero copyup data to save some I/O and network
* bandwidth -- zero copyup data is equivalent to the object not
dout("%s obj_req %p detected zeroes\n", __func__, obj_req);
bytes = 0;
}
-
- osd_req_op_cls_init(obj_req->osd_req, 0, CEPH_OSD_OP_CALL, "rbd",
- "copyup");
osd_req_op_cls_request_data_bvecs(obj_req->osd_req, 0,
obj_req->copyup_bvecs,
obj_req->copyup_bvec_count,
{
dout("%s rbd_dev %p\n", __func__, rbd_dev);
- cancel_delayed_work_sync(&rbd_dev->watch_dwork);
cancel_work_sync(&rbd_dev->acquired_lock_work);
cancel_work_sync(&rbd_dev->released_lock_work);
cancel_delayed_work_sync(&rbd_dev->lock_dwork);
rbd_dev->watch_state = RBD_WATCH_STATE_UNREGISTERED;
mutex_unlock(&rbd_dev->watch_mutex);
+ cancel_delayed_work_sync(&rbd_dev->watch_dwork);
ceph_osdc_flush_notifies(&rbd_dev->rbd_client->client->osdc);
}
corresponding drivers into the kernel.
To compile this driver as a module, choose M here and read
- <file:Documentation/parport.txt>. The module will be called lp.
+ <file:Documentation/admin-guide/parport.rst>. The module will be called lp.
If you have several parallel ports, you can specify which ports to
use with the "lp" kernel command line option. (Try "man bootparam"
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
- * Standard functionality for the common clock API. See Documentation/clk.txt
+ * Standard functionality for the common clock API. See Documentation/driver-api/clk.rst
*/
#include <linux/clk.h>
goto out;
}
- /* check that clk_ops are sane. See Documentation/clk.txt */
+ /* check that clk_ops are sane. See Documentation/driver-api/clk.rst */
if (core->ops->set_rate &&
!((core->ops->round_rate || core->ops->determine_rate) &&
core->ops->recalc_rate)) {
/**
* struct ingenic_clk - private data for a clock
- * @hw: see Documentation/clk.txt
+ * @hw: see Documentation/driver-api/clk.rst
* @cgu: a pointer to the CGU data
* @idx: the index of this clock in cgu->clock_info
*/
* Each device has a channels list, which runs unlocked but is never modified
* once the device is registered, it's just setup by the driver.
*
- * See Documentation/dmaengine.txt for more details
+ * See Documentation/driver-api/dmaengine for more details
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
DEFINE_DMI_ATTR_WITH_SHOW(product_version, 0444, DMI_PRODUCT_VERSION);
DEFINE_DMI_ATTR_WITH_SHOW(product_serial, 0400, DMI_PRODUCT_SERIAL);
DEFINE_DMI_ATTR_WITH_SHOW(product_uuid, 0400, DMI_PRODUCT_UUID);
+DEFINE_DMI_ATTR_WITH_SHOW(product_sku, 0444, DMI_PRODUCT_SKU);
DEFINE_DMI_ATTR_WITH_SHOW(product_family, 0444, DMI_PRODUCT_FAMILY);
DEFINE_DMI_ATTR_WITH_SHOW(board_vendor, 0444, DMI_BOARD_VENDOR);
DEFINE_DMI_ATTR_WITH_SHOW(board_name, 0444, DMI_BOARD_NAME);
ADD_DMI_ATTR(product_serial, DMI_PRODUCT_SERIAL);
ADD_DMI_ATTR(product_uuid, DMI_PRODUCT_UUID);
ADD_DMI_ATTR(product_family, DMI_PRODUCT_FAMILY);
+ ADD_DMI_ATTR(product_sku, DMI_PRODUCT_SKU);
ADD_DMI_ATTR(board_vendor, DMI_BOARD_VENDOR);
ADD_DMI_ATTR(board_name, DMI_BOARD_NAME);
ADD_DMI_ATTR(board_version, DMI_BOARD_VERSION);
dmi_save_ident(dm, DMI_PRODUCT_VERSION, 6);
dmi_save_ident(dm, DMI_PRODUCT_SERIAL, 7);
dmi_save_uuid(dm, DMI_PRODUCT_UUID, 8);
+ dmi_save_ident(dm, DMI_PRODUCT_SKU, 25);
dmi_save_ident(dm, DMI_PRODUCT_FAMILY, 26);
break;
case 2: /* Base Board Information */
* uses its own memory map instead.
*/
memblock_dump_all();
- memblock_remove(0, (phys_addr_t)ULLONG_MAX);
+ memblock_remove(0, PHYS_ADDR_MAX);
for_each_efi_memory_desc(md) {
paddr = md->phys_addr;
return 0;
}
-static inline u64 generic_id(unsigned long timestamp,
- unsigned int part, int count)
+static inline u64 generic_id(u64 timestamp, unsigned int part, int count)
{
- return ((u64) timestamp * 100 + part) * 1000 + count;
+ return (timestamp * 100 + part) * 1000 + count;
}
static int efi_pstore_read_func(struct efivar_entry *entry,
int i;
int cnt;
unsigned int part;
- unsigned long time, size;
+ unsigned long size;
+ u64 time;
if (efi_guidcmp(entry->var.VendorGuid, vendor))
return 0;
for (i = 0; i < DUMP_NAME_LEN; i++)
name[i] = entry->var.VariableName[i];
- if (sscanf(name, "dump-type%u-%u-%d-%lu-%c",
+ if (sscanf(name, "dump-type%u-%u-%d-%llu-%c",
&record->type, &part, &cnt, &time, &data_type) == 5) {
record->id = generic_id(time, part, cnt);
record->part = part;
else
record->compressed = false;
record->ecc_notice_size = 0;
- } else if (sscanf(name, "dump-type%u-%u-%d-%lu",
+ } else if (sscanf(name, "dump-type%u-%u-%d-%llu",
&record->type, &part, &cnt, &time) == 4) {
record->id = generic_id(time, part, cnt);
record->part = part;
record->time.tv_nsec = 0;
record->compressed = false;
record->ecc_notice_size = 0;
- } else if (sscanf(name, "dump-type%u-%u-%lu",
+ } else if (sscanf(name, "dump-type%u-%u-%llu",
&record->type, &part, &time) == 3) {
/*
* Check if an old format,
/* Since we copy the entire length of name, make sure it is wiped. */
memset(name, 0, sizeof(name));
- snprintf(name, sizeof(name), "dump-type%u-%u-%d-%lu-%c",
+ snprintf(name, sizeof(name), "dump-type%u-%u-%d-%lld-%c",
record->type, record->part, record->count,
- record->time.tv_sec, record->compressed ? 'C' : 'D');
+ (long long)record->time.tv_sec,
+ record->compressed ? 'C' : 'D');
for (i = 0; i < DUMP_NAME_LEN; i++)
efi_name[i] = name[i];
char name[DUMP_NAME_LEN];
int ret;
- snprintf(name, sizeof(name), "dump-type%u-%u-%d-%lu",
+ snprintf(name, sizeof(name), "dump-type%u-%u-%d-%lld",
record->type, record->part, record->count,
- record->time.tv_sec);
+ (long long)record->time.tv_sec);
ret = efi_pstore_erase_name(name);
if (ret != -ENOENT)
return ret;
- snprintf(name, sizeof(name), "dump-type%u-%u-%lu",
- record->type, record->part, record->time.tv_sec);
+ snprintf(name, sizeof(name), "dump-type%u-%u-%lld",
+ record->type, record->part, (long long)record->time.tv_sec);
ret = efi_pstore_erase_name(name);
return ret;
mem_info->local_mem_size_public,
mem_info->local_mem_size_private);
- if (amdgpu_emu_mode == 1) {
- mem_info->mem_clk_max = 100;
- return;
- }
-
if (amdgpu_sriov_vf(adev))
mem_info->mem_clk_max = adev->clock.default_mclk / 100;
- else
+ else if (adev->powerplay.pp_funcs)
mem_info->mem_clk_max = amdgpu_dpm_get_mclk(adev, false) / 100;
+ else
+ mem_info->mem_clk_max = 100;
}
uint64_t get_gpu_clock_counter(struct kgd_dev *kgd)
struct amdgpu_device *adev = (struct amdgpu_device *)kgd;
/* the sclk is in quantas of 10kHz */
- if (amdgpu_emu_mode == 1)
- return 100;
-
if (amdgpu_sriov_vf(adev))
return adev->clock.default_sclk / 100;
-
- return amdgpu_dpm_get_sclk(adev, false) / 100;
+ else if (adev->powerplay.pp_funcs)
+ return amdgpu_dpm_get_sclk(adev, false) / 100;
+ else
+ return 100;
}
void get_cu_info(struct kgd_dev *kgd, struct kfd_cu_info *cu_info)
{ 0x1002, 0x6900, 0x1002, 0x0124, AMDGPU_PX_QUIRK_FORCE_ATPX },
{ 0x1002, 0x6900, 0x1028, 0x0812, AMDGPU_PX_QUIRK_FORCE_ATPX },
{ 0x1002, 0x6900, 0x1028, 0x0813, AMDGPU_PX_QUIRK_FORCE_ATPX },
- { 0x1002, 0x67DF, 0x1028, 0x0774, AMDGPU_PX_QUIRK_FORCE_ATPX },
{ 0, 0, 0, 0, 0 },
};
struct amdgpu_bo_list_entry *e;
struct list_head duplicates;
unsigned i, tries = 10;
+ struct amdgpu_bo *gds;
+ struct amdgpu_bo *gws;
+ struct amdgpu_bo *oa;
int r;
INIT_LIST_HEAD(&p->validated);
amdgpu_cs_report_moved_bytes(p->adev, p->bytes_moved,
p->bytes_moved_vis);
+
if (p->bo_list) {
- struct amdgpu_bo *gds = p->bo_list->gds_obj;
- struct amdgpu_bo *gws = p->bo_list->gws_obj;
- struct amdgpu_bo *oa = p->bo_list->oa_obj;
struct amdgpu_vm *vm = &fpriv->vm;
unsigned i;
+ gds = p->bo_list->gds_obj;
+ gws = p->bo_list->gws_obj;
+ oa = p->bo_list->oa_obj;
for (i = 0; i < p->bo_list->num_entries; i++) {
struct amdgpu_bo *bo = p->bo_list->array[i].robj;
p->bo_list->array[i].bo_va = amdgpu_vm_bo_find(vm, bo);
}
+ } else {
+ gds = p->adev->gds.gds_gfx_bo;
+ gws = p->adev->gds.gws_gfx_bo;
+ oa = p->adev->gds.oa_gfx_bo;
+ }
- if (gds) {
- p->job->gds_base = amdgpu_bo_gpu_offset(gds);
- p->job->gds_size = amdgpu_bo_size(gds);
- }
- if (gws) {
- p->job->gws_base = amdgpu_bo_gpu_offset(gws);
- p->job->gws_size = amdgpu_bo_size(gws);
- }
- if (oa) {
- p->job->oa_base = amdgpu_bo_gpu_offset(oa);
- p->job->oa_size = amdgpu_bo_size(oa);
- }
+ if (gds) {
+ p->job->gds_base = amdgpu_bo_gpu_offset(gds);
+ p->job->gds_size = amdgpu_bo_size(gds);
+ }
+ if (gws) {
+ p->job->gws_base = amdgpu_bo_gpu_offset(gws);
+ p->job->gws_size = amdgpu_bo_size(gws);
+ }
+ if (oa) {
+ p->job->oa_base = amdgpu_bo_gpu_offset(oa);
+ p->job->oa_size = amdgpu_bo_size(oa);
}
if (!r && p->uf_entry.robj) {
}
}
}
+
+ if (adev->powerplay.pp_feature & PP_GFXOFF_MASK) {
+ /* enable gfx powergating */
+ amdgpu_device_ip_set_powergating_state(adev,
+ AMD_IP_BLOCK_TYPE_GFX,
+ AMD_PG_STATE_GATE);
+ /* enable gfxoff */
+ amdgpu_device_ip_set_powergating_state(adev,
+ AMD_IP_BLOCK_TYPE_SMC,
+ AMD_PG_STATE_GATE);
+ }
+
return 0;
}
#include <drm/drmP.h>
#include <drm/amdgpu_drm.h>
#include "amdgpu.h"
+#include "amdgpu_display.h"
void amdgpu_gem_object_free(struct drm_gem_object *gobj)
{
/* create a gem object to contain this object in */
if (args->in.domains & (AMDGPU_GEM_DOMAIN_GDS |
AMDGPU_GEM_DOMAIN_GWS | AMDGPU_GEM_DOMAIN_OA)) {
+ if (flags & AMDGPU_GEM_CREATE_VM_ALWAYS_VALID) {
+ /* if gds bo is created from user space, it must be
+ * passed to bo list
+ */
+ DRM_ERROR("GDS bo cannot be per-vm-bo\n");
+ return -EINVAL;
+ }
flags |= AMDGPU_GEM_CREATE_NO_CPU_ACCESS;
if (args->in.domains == AMDGPU_GEM_DOMAIN_GDS)
size = size << AMDGPU_GDS_SHIFT;
struct amdgpu_device *adev = dev->dev_private;
struct drm_gem_object *gobj;
uint32_t handle;
+ u32 domain;
int r;
args->pitch = amdgpu_align_pitch(adev, args->width,
DIV_ROUND_UP(args->bpp, 8), 0);
args->size = (u64)args->pitch * args->height;
args->size = ALIGN(args->size, PAGE_SIZE);
-
- r = amdgpu_gem_object_create(adev, args->size, 0,
- AMDGPU_GEM_DOMAIN_VRAM,
+ domain = amdgpu_bo_get_preferred_pin_domain(adev,
+ amdgpu_display_supported_domains(adev));
+ r = amdgpu_gem_object_create(adev, args->size, 0, domain,
AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED,
false, NULL, &gobj);
if (r)
/* This assumes only APU display buffers are pinned with (VRAM|GTT).
* See function amdgpu_display_supported_domains()
*/
- if (domain == (AMDGPU_GEM_DOMAIN_VRAM | AMDGPU_GEM_DOMAIN_GTT)) {
- domain = AMDGPU_GEM_DOMAIN_VRAM;
- if (adev->gmc.real_vram_size <= AMDGPU_SG_THRESHOLD)
- domain = AMDGPU_GEM_DOMAIN_GTT;
- }
+ domain = amdgpu_bo_get_preferred_pin_domain(adev, domain);
if (bo->pin_count) {
uint32_t mem_type = bo->tbo.mem.mem_type;
return bo->tbo.offset;
}
+
+uint32_t amdgpu_bo_get_preferred_pin_domain(struct amdgpu_device *adev,
+ uint32_t domain)
+{
+ if (domain == (AMDGPU_GEM_DOMAIN_VRAM | AMDGPU_GEM_DOMAIN_GTT)) {
+ domain = AMDGPU_GEM_DOMAIN_VRAM;
+ if (adev->gmc.real_vram_size <= AMDGPU_SG_THRESHOLD)
+ domain = AMDGPU_GEM_DOMAIN_GTT;
+ }
+ return domain;
+}
struct reservation_object *resv,
struct dma_fence **fence,
bool direct);
-
+uint32_t amdgpu_bo_get_preferred_pin_domain(struct amdgpu_device *adev,
+ uint32_t domain);
/*
* sub allocation
int amdgpu_vcn_sw_init(struct amdgpu_device *adev)
{
- struct amdgpu_ring *ring;
- struct drm_sched_rq *rq;
unsigned long bo_size;
const char *fw_name;
const struct common_firmware_header *hdr;
}
hdr = (const struct common_firmware_header *)adev->vcn.fw->data;
+ adev->vcn.fw_version = le32_to_cpu(hdr->ucode_version);
family_id = le32_to_cpu(hdr->ucode_version) & 0xff;
version_major = (le32_to_cpu(hdr->ucode_version) >> 24) & 0xff;
version_minor = (le32_to_cpu(hdr->ucode_version) >> 8) & 0xff;
return r;
}
- ring = &adev->vcn.ring_dec;
- rq = &ring->sched.sched_rq[DRM_SCHED_PRIORITY_NORMAL];
- r = drm_sched_entity_init(&ring->sched, &adev->vcn.entity_dec,
- rq, NULL);
- if (r != 0) {
- DRM_ERROR("Failed setting up VCN dec run queue.\n");
- return r;
- }
-
- ring = &adev->vcn.ring_enc[0];
- rq = &ring->sched.sched_rq[DRM_SCHED_PRIORITY_NORMAL];
- r = drm_sched_entity_init(&ring->sched, &adev->vcn.entity_enc,
- rq, NULL);
- if (r != 0) {
- DRM_ERROR("Failed setting up VCN enc run queue.\n");
- return r;
- }
-
return 0;
}
kfree(adev->vcn.saved_bo);
- drm_sched_entity_fini(&adev->vcn.ring_dec.sched, &adev->vcn.entity_dec);
-
- drm_sched_entity_fini(&adev->vcn.ring_enc[0].sched, &adev->vcn.entity_enc);
-
amdgpu_bo_free_kernel(&adev->vcn.vcpu_bo,
&adev->vcn.gpu_addr,
(void **)&adev->vcn.cpu_addr);
}
static int amdgpu_vcn_dec_send_msg(struct amdgpu_ring *ring,
- struct amdgpu_bo *bo, bool direct,
+ struct amdgpu_bo *bo,
struct dma_fence **fence)
{
struct amdgpu_device *adev = ring->adev;
}
ib->length_dw = 16;
- if (direct) {
- r = amdgpu_ib_schedule(ring, 1, ib, NULL, &f);
- job->fence = dma_fence_get(f);
- if (r)
- goto err_free;
+ r = amdgpu_ib_schedule(ring, 1, ib, NULL, &f);
+ job->fence = dma_fence_get(f);
+ if (r)
+ goto err_free;
- amdgpu_job_free(job);
- } else {
- r = amdgpu_job_submit(job, ring, &adev->vcn.entity_dec,
- AMDGPU_FENCE_OWNER_UNDEFINED, &f);
- if (r)
- goto err_free;
- }
+ amdgpu_job_free(job);
amdgpu_bo_fence(bo, f, false);
amdgpu_bo_unreserve(bo);
for (i = 14; i < 1024; ++i)
msg[i] = cpu_to_le32(0x0);
- return amdgpu_vcn_dec_send_msg(ring, bo, true, fence);
+ return amdgpu_vcn_dec_send_msg(ring, bo, fence);
}
static int amdgpu_vcn_dec_get_destroy_msg(struct amdgpu_ring *ring, uint32_t handle,
- bool direct, struct dma_fence **fence)
+ struct dma_fence **fence)
{
struct amdgpu_device *adev = ring->adev;
struct amdgpu_bo *bo = NULL;
for (i = 6; i < 1024; ++i)
msg[i] = cpu_to_le32(0x0);
- return amdgpu_vcn_dec_send_msg(ring, bo, direct, fence);
+ return amdgpu_vcn_dec_send_msg(ring, bo, fence);
}
int amdgpu_vcn_dec_ring_test_ib(struct amdgpu_ring *ring, long timeout)
goto error;
}
- r = amdgpu_vcn_dec_get_destroy_msg(ring, 1, true, &fence);
+ r = amdgpu_vcn_dec_get_destroy_msg(ring, 1, &fence);
if (r) {
DRM_ERROR("amdgpu: failed to get destroy ib (%ld).\n", r);
goto error;
struct amdgpu_ring ring_dec;
struct amdgpu_ring ring_enc[AMDGPU_VCN_MAX_ENC_RINGS];
struct amdgpu_irq_src irq;
- struct drm_sched_entity entity_dec;
- struct drm_sched_entity entity_enc;
unsigned num_enc_rings;
};
before->last = saddr - 1;
before->offset = tmp->offset;
before->flags = tmp->flags;
- list_add(&before->list, &tmp->list);
+ before->bo_va = tmp->bo_va;
+ list_add(&before->list, &tmp->bo_va->invalids);
}
/* Remember mapping split at the end */
after->offset = tmp->offset;
after->offset += after->start - tmp->start;
after->flags = tmp->flags;
- list_add(&after->list, &tmp->list);
+ after->bo_va = tmp->bo_va;
+ list_add(&after->list, &tmp->bo_va->invalids);
}
list_del(&tmp->list);
int fb_channel_number;
fb_channel_number = adev->df_funcs->get_fb_channel_number(adev);
- if (fb_channel_number > ARRAY_SIZE(df_v3_6_channel_number))
+ if (fb_channel_number >= ARRAY_SIZE(df_v3_6_channel_number))
fb_channel_number = 0;
return df_v3_6_channel_number[fb_channel_number];
static const struct soc15_reg_golden golden_settings_gc_9_0_vg20[] =
{
+ SOC15_REG_GOLDEN_VALUE(GC, 0, mmCB_DCC_CONFIG, 0x0f000080, 0x04000080),
SOC15_REG_GOLDEN_VALUE(GC, 0, mmCB_HW_CONTROL_2, 0x0f000000, 0x0a000000),
SOC15_REG_GOLDEN_VALUE(GC, 0, mmCB_HW_CONTROL_3, 0x30000000, 0x10000000),
SOC15_REG_GOLDEN_VALUE(GC, 0, mmGB_ADDR_CONFIG, 0xf3e777ff, 0x22014042),
int indirect_offset,
int list_size,
int *unique_indirect_regs,
- int *unique_indirect_reg_count,
+ int unique_indirect_reg_count,
int *indirect_start_offsets,
- int *indirect_start_offsets_count)
+ int *indirect_start_offsets_count,
+ int max_start_offsets_count)
{
int idx;
for (; indirect_offset < list_size; indirect_offset++) {
+ WARN_ON(*indirect_start_offsets_count >= max_start_offsets_count);
indirect_start_offsets[*indirect_start_offsets_count] = indirect_offset;
*indirect_start_offsets_count = *indirect_start_offsets_count + 1;
indirect_offset += 2;
/* look for the matching indice */
- for (idx = 0; idx < *unique_indirect_reg_count; idx++) {
+ for (idx = 0; idx < unique_indirect_reg_count; idx++) {
if (unique_indirect_regs[idx] ==
register_list_format[indirect_offset] ||
!unique_indirect_regs[idx])
break;
}
- BUG_ON(idx >= *unique_indirect_reg_count);
+ BUG_ON(idx >= unique_indirect_reg_count);
if (!unique_indirect_regs[idx])
unique_indirect_regs[idx] = register_list_format[indirect_offset];
adev->gfx.rlc.reg_list_format_direct_reg_list_length,
adev->gfx.rlc.reg_list_format_size_bytes >> 2,
unique_indirect_regs,
- &unique_indirect_reg_count,
+ unique_indirect_reg_count,
indirect_start_offsets,
- &indirect_start_offsets_count);
+ &indirect_start_offsets_count,
+ ARRAY_SIZE(indirect_start_offsets));
/* enable auto inc in case it is disabled */
tmp = RREG32(SOC15_REG_OFFSET(GC, 0, mmRLC_SRM_CNTL));
if (r)
return r;
- r = amdgpu_device_ip_set_powergating_state(adev, AMD_IP_BLOCK_TYPE_GFX,
- AMD_PG_STATE_GATE);
- if (r)
- return r;
-
return 0;
}
#define smnMP1_FIRMWARE_FLAGS 0x3010028
+static uint32_t sos_old_versions[] = {1517616, 1510592, 1448594, 1446554};
+
static int
psp_v3_1_get_fw_type(struct amdgpu_firmware_info *ucode, enum psp_gfx_fw_type *type)
{
return ret;
}
+static bool psp_v3_1_match_version(struct amdgpu_device *adev, uint32_t ver)
+{
+ int i;
+
+ if (ver == adev->psp.sos_fw_version)
+ return true;
+
+ /*
+ * Double check if the latest four legacy versions.
+ * If yes, it is still the right version.
+ */
+ for (i = 0; i < sizeof(sos_old_versions) / sizeof(uint32_t); i++) {
+ if (sos_old_versions[i] == adev->psp.sos_fw_version)
+ return true;
+ }
+
+ return false;
+}
+
static int psp_v3_1_bootloader_load_sos(struct psp_context *psp)
{
int ret;
unsigned int psp_gfxdrv_command_reg = 0;
struct amdgpu_device *adev = psp->adev;
- uint32_t sol_reg;
+ uint32_t sol_reg, ver;
/* Check sOS sign of life register to confirm sys driver and sOS
* are already been loaded.
RREG32_SOC15(MP0, 0, mmMP0_SMN_C2PMSG_81),
0, true);
+ ver = RREG32_SOC15(MP0, 0, mmMP0_SMN_C2PMSG_58);
+ if (!psp_v3_1_match_version(adev, ver))
+ DRM_WARN("SOS version doesn't match\n");
+
return ret;
}
AMD_CG_SUPPORT_BIF_MGCG |
AMD_CG_SUPPORT_BIF_LS |
AMD_CG_SUPPORT_HDP_MGCG |
+ AMD_CG_SUPPORT_HDP_LS |
AMD_CG_SUPPORT_ROM_MGCG |
AMD_CG_SUPPORT_VCE_MGCG |
AMD_CG_SUPPORT_UVD_MGCG;
return 0;
}
-bool vcn_v1_0_is_idle(void *handle)
+static bool vcn_v1_0_is_idle(void *handle)
{
struct amdgpu_device *adev = (struct amdgpu_device *)handle;
return (RREG32_SOC15(VCN, 0, mmUVD_STATUS) == 0x2);
}
-int vcn_v1_0_wait_for_idle(void *handle)
+static int vcn_v1_0_wait_for_idle(void *handle)
{
struct amdgpu_device *adev = (struct amdgpu_device *)handle;
int ret = 0;
#include <linux/moduleparam.h>
#include <linux/version.h>
#include <linux/types.h>
+#include <linux/pm_runtime.h>
#include <drm/drmP.h>
#include <drm/drm_atomic.h>
{
uint32_t bpc = connector->display_info.bpc;
- /* Limited color depth to 8bit
- * TODO: Still need to handle deep color
- */
- if (bpc > 8)
- bpc = 8;
-
switch (bpc) {
case 0:
/* Temporary Work around, DRM don't parse color depth for
}
}
-static int create_fake_sink(struct amdgpu_dm_connector *aconnector)
+static struct dc_sink *
+create_fake_sink(struct amdgpu_dm_connector *aconnector)
{
- struct dc_sink *sink = NULL;
struct dc_sink_init_data sink_init_data = { 0 };
-
+ struct dc_sink *sink = NULL;
sink_init_data.link = aconnector->dc_link;
sink_init_data.sink_signal = aconnector->dc_link->connector_signal;
sink = dc_sink_create(&sink_init_data);
if (!sink) {
DRM_ERROR("Failed to create sink!\n");
- return -ENOMEM;
+ return NULL;
}
-
sink->sink_signal = SIGNAL_TYPE_VIRTUAL;
- aconnector->fake_enable = true;
-
- aconnector->dc_sink = sink;
- aconnector->dc_link->local_sink = sink;
- return 0;
+ return sink;
}
static void set_multisync_trigger_params(
struct dc_stream_state *stream = NULL;
struct drm_display_mode mode = *drm_mode;
bool native_mode_found = false;
-
+ struct dc_sink *sink = NULL;
if (aconnector == NULL) {
DRM_ERROR("aconnector is NULL!\n");
return stream;
return stream;
}
- if (create_fake_sink(aconnector))
+ sink = create_fake_sink(aconnector);
+ if (!sink)
return stream;
+ } else {
+ sink = aconnector->dc_sink;
}
- stream = dc_create_stream_for_sink(aconnector->dc_sink);
+ stream = dc_create_stream_for_sink(sink);
if (stream == NULL) {
DRM_ERROR("Failed to create stream for sink!\n");
- return stream;
+ goto finish;
}
list_for_each_entry(preferred_mode, &aconnector->base.modes, head) {
fill_audio_info(
&stream->audio_info,
drm_connector,
- aconnector->dc_sink);
+ sink);
update_stream_signal(stream);
if (dm_state && dm_state->freesync_capable)
stream->ignore_msa_timing_param = true;
+finish:
+ if (sink && sink->sink_signal == SIGNAL_TYPE_VIRTUAL)
+ dc_sink_release(sink);
return stream;
}
struct dm_connector_state *state =
to_dm_connector_state(connector->state);
+ if (connector->state)
+ __drm_atomic_helper_connector_destroy_state(connector->state);
+
kfree(state);
state = kzalloc(sizeof(*state), GFP_KERNEL);
state->underscan_hborder = 0;
state->underscan_vborder = 0;
- connector->state = &state->base;
- connector->state->connector = connector;
+ __drm_atomic_helper_connector_reset(connector, &state->base);
}
}
}
}
- /* It's a hack for s3 since in 4.9 kernel filter out cursor buffer
- * prepare and cleanup in drm_atomic_helper_prepare_planes
- * and drm_atomic_helper_cleanup_planes because fb doens't in s3.
- * IN 4.10 kernel this code should be removed and amdgpu_device_suspend
- * code touching fram buffers should be avoided for DC.
- */
- if (plane->type == DRM_PLANE_TYPE_CURSOR) {
- struct amdgpu_crtc *acrtc = to_amdgpu_crtc(new_state->crtc);
-
- acrtc->cursor_bo = obj;
- }
return 0;
}
if (dm_old_crtc_state->stream)
remove_stream(adev, acrtc, dm_old_crtc_state->stream);
+ pm_runtime_get_noresume(dev->dev);
+
acrtc->enabled = true;
acrtc->hw_mode = new_crtc_state->mode;
crtc->hwmode = new_crtc_state->mode;
drm_atomic_helper_wait_for_flip_done(dev, state);
drm_atomic_helper_cleanup_planes(dev, state);
+
+ /* Finally, drop a runtime PM reference for each newly disabled CRTC,
+ * so we can put the GPU into runtime suspend if we're not driving any
+ * displays anymore
+ */
+ pm_runtime_mark_last_busy(dev->dev);
+ for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) {
+ if (old_crtc_state->active && !new_crtc_state->active)
+ pm_runtime_put_autosuspend(dev->dev);
+ }
}
return 0;
}
+ if (acrtc->otg_inst == -1)
+ return 0;
+
irq_source = dal_irq_type + acrtc->otg_inst;
st = (state == AMDGPU_IRQ_STATE_ENABLE);
}
}
+static void pp_to_dc_clock_levels_with_latency(
+ const struct pp_clock_levels_with_latency *pp_clks,
+ struct dm_pp_clock_levels_with_latency *clk_level_info,
+ enum dm_pp_clock_type dc_clk_type)
+{
+ uint32_t i;
+
+ if (pp_clks->num_levels > DM_PP_MAX_CLOCK_LEVELS) {
+ DRM_INFO("DM_PPLIB: Warning: %s clock: number of levels %d exceeds maximum of %d!\n",
+ DC_DECODE_PP_CLOCK_TYPE(dc_clk_type),
+ pp_clks->num_levels,
+ DM_PP_MAX_CLOCK_LEVELS);
+
+ clk_level_info->num_levels = DM_PP_MAX_CLOCK_LEVELS;
+ } else
+ clk_level_info->num_levels = pp_clks->num_levels;
+
+ DRM_DEBUG("DM_PPLIB: values for %s clock\n",
+ DC_DECODE_PP_CLOCK_TYPE(dc_clk_type));
+
+ for (i = 0; i < clk_level_info->num_levels; i++) {
+ DRM_DEBUG("DM_PPLIB:\t %d\n", pp_clks->data[i].clocks_in_khz);
+ clk_level_info->data[i].clocks_in_khz = pp_clks->data[i].clocks_in_khz;
+ clk_level_info->data[i].latency_in_us = pp_clks->data[i].latency_in_us;
+ }
+}
+
bool dm_pp_get_clock_levels_by_type(
const struct dc_context *ctx,
enum dm_pp_clock_type clk_type,
enum dm_pp_clock_type clk_type,
struct dm_pp_clock_levels_with_latency *clk_level_info)
{
- /* TODO: to be implemented */
- return false;
+ struct amdgpu_device *adev = ctx->driver_context;
+ void *pp_handle = adev->powerplay.pp_handle;
+ struct pp_clock_levels_with_latency pp_clks = { 0 };
+ const struct amd_pm_funcs *pp_funcs = adev->powerplay.pp_funcs;
+
+ if (!pp_funcs || !pp_funcs->get_clock_by_type_with_latency)
+ return false;
+
+ if (pp_funcs->get_clock_by_type_with_latency(pp_handle,
+ dc_to_pp_clock_type(clk_type),
+ &pp_clks))
+ return false;
+
+ pp_to_dc_clock_levels_with_latency(&pp_clks, clk_level_info, clk_type);
+
+ return true;
}
bool dm_pp_get_clock_levels_by_type_with_voltage(
return min_clamp;
}
+unsigned int dc_fixpt_u3d19(struct fixed31_32 arg)
+{
+ return ux_dy(arg.value, 3, 19);
+}
+
unsigned int dc_fixpt_u2d19(struct fixed31_32 arg)
{
return ux_dy(arg.value, 2, 19);
struct dc_link *link,
union hpd_irq_data *irq_data)
{
+ static enum dc_status retval;
+
/* The HW reads 16 bytes from 200h on HPD,
* but if we get an AUX_DEFER, the HW cannot retry
* and this causes the CTS tests 4.3.2.1 - 3.2.4 to
* fail, so we now explicitly read 6 bytes which is
* the req from the above mentioned test cases.
+ *
+ * For DP 1.4 we need to read those from 2002h range.
*/
- return core_link_read_dpcd(
- link,
- DP_SINK_COUNT,
- irq_data->raw,
- sizeof(union hpd_irq_data));
+ if (link->dpcd_caps.dpcd_rev.raw < DPCD_REV_14)
+ retval = core_link_read_dpcd(
+ link,
+ DP_SINK_COUNT,
+ irq_data->raw,
+ sizeof(union hpd_irq_data));
+ else {
+ /* Read 2 bytes at this location,... */
+ retval = core_link_read_dpcd(
+ link,
+ DP_SINK_COUNT_ESI,
+ irq_data->raw,
+ 2);
+
+ if (retval != DC_OK)
+ return retval;
+
+ /* ... then read remaining 4 at the other location */
+ retval = core_link_read_dpcd(
+ link,
+ DP_LANE0_1_STATUS_ESI,
+ &irq_data->raw[2],
+ 4);
+ }
+
+ return retval;
}
static bool allow_hpd_rx_irq(const struct dc_link *link)
static bool retrieve_link_cap(struct dc_link *link)
{
- uint8_t dpcd_data[DP_TRAINING_AUX_RD_INTERVAL - DP_DPCD_REV + 1];
+ uint8_t dpcd_data[DP_ADAPTER_CAP - DP_DPCD_REV + 1];
union down_stream_port_count down_strm_port_count;
union edp_configuration_cap edp_config_cap;
uint32_t max_retries = 50;
/*we need turn on clock before programming AFMT block*/
- REG_UPDATE(AFMT_CNTL, AFMT_AUDIO_CLOCK_EN, 1);
+ if (REG(AFMT_CNTL))
+ REG_UPDATE(AFMT_CNTL, AFMT_AUDIO_CLOCK_EN, 1);
if (REG(AFMT_VBI_PACKET_CONTROL1)) {
if (packet_index >= 8)
const uint32_t *content =
(const uint32_t *) &info_frame->avi.sb[0];
/*we need turn on clock before programming AFMT block*/
- REG_UPDATE(AFMT_CNTL, AFMT_AUDIO_CLOCK_EN, 1);
+ if (REG(AFMT_CNTL))
+ REG_UPDATE(AFMT_CNTL, AFMT_AUDIO_CLOCK_EN, 1);
REG_WRITE(AFMT_AVI_INFO0, content[0]);
frame_count = dm_read_reg(ctx, mmCRTC_STATUS_FRAME_COUNT);
- for (retry = 100; retry > 0; retry--) {
+ for (retry = 10000; retry > 0; retry--) {
if (frame_count != dm_read_reg(ctx, mmCRTC_STATUS_FRAME_COUNT))
break;
- msleep(1);
+ udelay(10);
}
if (!retry)
dm_error("Frame count did not increase for 100ms.\n");
uint32_t addr = mmFBC_STATUS;
uint32_t value;
- while (counter < 10) {
+ while (counter < 1000) {
value = dm_read_reg(cp110->base.ctx, addr);
if (get_reg_field_value(
value,
FBC_STATUS,
FBC_ENABLE_STATUS) == enabled)
break;
- msleep(10);
+ udelay(100);
counter++;
}
/*don't free audio if it is from retrain or internal disable stream*/
if (option == FREE_ACQUIRED_RESOURCE && dc->caps.dynamic_audio == true) {
/*we have to dynamic arbitrate the audio endpoints*/
- pipe_ctx->stream_res.audio = NULL;
/*we free the resource, need reset is_audio_acquired*/
update_audio_usage(&dc->current_state->res_ctx, dc->res_pool, pipe_ctx->stream_res.audio, false);
+ pipe_ctx->stream_res.audio = NULL;
}
/* TODO: notify audio driver for if audio modes list changed
#define IDENTITY_RATIO(ratio) (dc_fixpt_u2d19(ratio) == (1 << 19))
-
-bool dpp_get_optimal_number_of_taps(
+static bool dpp_get_optimal_number_of_taps(
struct dpp *dpp,
struct scaler_data *scl_data,
const struct scaling_taps *in_taps)
enum ipp_degamma_mode mode);
void dpp1_set_degamma_pwl(struct dpp *dpp_base,
- const struct pwl_params *params);
+ const struct pwl_params *params);
-bool dpp_get_optimal_number_of_taps(
- struct dpp *dpp,
- struct scaler_data *scl_data,
- const struct scaling_taps *in_taps);
void dpp_read_state(struct dpp *dpp_base,
struct dcn_dpp_state *s);
uint32_t init_int = 0;
REG_SET(SCL_HORZ_FILTER_SCALE_RATIO, 0,
- SCL_H_SCALE_RATIO, dc_fixpt_u2d19(data->ratios.horz) << 5);
+ SCL_H_SCALE_RATIO, dc_fixpt_u3d19(data->ratios.horz) << 5);
REG_SET(SCL_VERT_FILTER_SCALE_RATIO, 0,
- SCL_V_SCALE_RATIO, dc_fixpt_u2d19(data->ratios.vert) << 5);
+ SCL_V_SCALE_RATIO, dc_fixpt_u3d19(data->ratios.vert) << 5);
REG_SET(SCL_HORZ_FILTER_SCALE_RATIO_C, 0,
- SCL_H_SCALE_RATIO_C, dc_fixpt_u2d19(data->ratios.horz_c) << 5);
+ SCL_H_SCALE_RATIO_C, dc_fixpt_u3d19(data->ratios.horz_c) << 5);
REG_SET(SCL_VERT_FILTER_SCALE_RATIO_C, 0,
- SCL_V_SCALE_RATIO_C, dc_fixpt_u2d19(data->ratios.vert_c) << 5);
+ SCL_V_SCALE_RATIO_C, dc_fixpt_u3d19(data->ratios.vert_c) << 5);
/*
* 0.24 format for fraction, first five bits zeroed
if (address->grph_stereo.right_addr.quad_part == 0)
break;
- REG_UPDATE_4(DCSURF_SURFACE_CONTROL,
+ REG_UPDATE_8(DCSURF_SURFACE_CONTROL,
PRIMARY_SURFACE_TMZ, address->tmz_surface,
PRIMARY_SURFACE_TMZ_C, address->tmz_surface,
PRIMARY_META_SURFACE_TMZ, address->tmz_surface,
- PRIMARY_META_SURFACE_TMZ_C, address->tmz_surface);
+ PRIMARY_META_SURFACE_TMZ_C, address->tmz_surface,
+ SECONDARY_SURFACE_TMZ, address->tmz_surface,
+ SECONDARY_SURFACE_TMZ_C, address->tmz_surface,
+ SECONDARY_META_SURFACE_TMZ, address->tmz_surface,
+ SECONDARY_META_SURFACE_TMZ_C, address->tmz_surface);
if (address->grph_stereo.right_meta_addr.quad_part != 0) {
uint32_t dcc_ind_64b_blk = independent_64b_blks ? 1 : 0;
struct dcn10_hubp *hubp1 = TO_DCN10_HUBP(hubp);
- REG_UPDATE_2(DCSURF_SURFACE_CONTROL,
+ REG_UPDATE_4(DCSURF_SURFACE_CONTROL,
PRIMARY_SURFACE_DCC_EN, dcc_en,
- PRIMARY_SURFACE_DCC_IND_64B_BLK, dcc_ind_64b_blk);
+ PRIMARY_SURFACE_DCC_IND_64B_BLK, dcc_ind_64b_blk,
+ SECONDARY_SURFACE_DCC_EN, dcc_en,
+ SECONDARY_SURFACE_DCC_IND_64B_BLK, dcc_ind_64b_blk);
}
void hubp1_program_surface_config(
HUBP_SF(HUBPREQ0_DCSURF_SURFACE_CONTROL, PRIMARY_META_SURFACE_TMZ_C, mask_sh),\
HUBP_SF(HUBPREQ0_DCSURF_SURFACE_CONTROL, PRIMARY_SURFACE_DCC_EN, mask_sh),\
HUBP_SF(HUBPREQ0_DCSURF_SURFACE_CONTROL, PRIMARY_SURFACE_DCC_IND_64B_BLK, mask_sh),\
+ HUBP_SF(HUBPREQ0_DCSURF_SURFACE_CONTROL, SECONDARY_SURFACE_TMZ, mask_sh),\
+ HUBP_SF(HUBPREQ0_DCSURF_SURFACE_CONTROL, SECONDARY_SURFACE_TMZ_C, mask_sh),\
+ HUBP_SF(HUBPREQ0_DCSURF_SURFACE_CONTROL, SECONDARY_META_SURFACE_TMZ, mask_sh),\
+ HUBP_SF(HUBPREQ0_DCSURF_SURFACE_CONTROL, SECONDARY_META_SURFACE_TMZ_C, mask_sh),\
+ HUBP_SF(HUBPREQ0_DCSURF_SURFACE_CONTROL, SECONDARY_SURFACE_DCC_EN, mask_sh),\
+ HUBP_SF(HUBPREQ0_DCSURF_SURFACE_CONTROL, SECONDARY_SURFACE_DCC_IND_64B_BLK, mask_sh),\
HUBP_SF(HUBPRET0_HUBPRET_CONTROL, DET_BUF_PLANE1_BASE_ADDRESS, mask_sh),\
HUBP_SF(HUBPRET0_HUBPRET_CONTROL, CROSSBAR_SRC_CB_B, mask_sh),\
HUBP_SF(HUBPRET0_HUBPRET_CONTROL, CROSSBAR_SRC_CR_R, mask_sh),\
type SECONDARY_META_SURFACE_TMZ_C;\
type PRIMARY_SURFACE_DCC_EN;\
type PRIMARY_SURFACE_DCC_IND_64B_BLK;\
+ type SECONDARY_SURFACE_DCC_EN;\
+ type SECONDARY_SURFACE_DCC_IND_64B_BLK;\
type DET_BUF_PLANE1_BASE_ADDRESS;\
type CROSSBAR_SRC_CB_B;\
type CROSSBAR_SRC_CR_R;\
REG_UPDATE(DP_PIXEL_FORMAT, DP_COMPONENT_DEPTH,
DP_COMPONENT_PIXEL_DEPTH_12BPC);
break;
+ case COLOR_DEPTH_161616:
+ REG_UPDATE(DP_PIXEL_FORMAT, DP_COMPONENT_DEPTH,
+ DP_COMPONENT_PIXEL_DEPTH_16BPC);
+ break;
default:
REG_UPDATE(DP_PIXEL_FORMAT, DP_COMPONENT_DEPTH,
DP_COMPONENT_PIXEL_DEPTH_6BPC);
* fractional
*/
+unsigned int dc_fixpt_u3d19(struct fixed31_32 arg);
+
unsigned int dc_fixpt_u2d19(struct fixed31_32 arg);
unsigned int dc_fixpt_u0d19(struct fixed31_32 arg);
/* DF_CS_AON0_DramBaseAddress0 */
#define DF_CS_UMC_AON0_DramBaseAddress0__AddrRngVal__SHIFT 0x0
#define DF_CS_UMC_AON0_DramBaseAddress0__LgcyMmioHoleEn__SHIFT 0x1
-#define DF_CS_UMC_AON0_DramBaseAddress0__IntLvNumChan__SHIFT 0x4
-#define DF_CS_UMC_AON0_DramBaseAddress0__IntLvAddrSel__SHIFT 0x8
+#define DF_CS_UMC_AON0_DramBaseAddress0__IntLvNumChan__SHIFT 0x2
+#define DF_CS_UMC_AON0_DramBaseAddress0__IntLvAddrSel__SHIFT 0x9
#define DF_CS_UMC_AON0_DramBaseAddress0__DramBaseAddr__SHIFT 0xc
#define DF_CS_UMC_AON0_DramBaseAddress0__AddrRngVal_MASK 0x00000001L
#define DF_CS_UMC_AON0_DramBaseAddress0__LgcyMmioHoleEn_MASK 0x00000002L
-#define DF_CS_UMC_AON0_DramBaseAddress0__IntLvNumChan_MASK 0x000000F0L
-#define DF_CS_UMC_AON0_DramBaseAddress0__IntLvAddrSel_MASK 0x00000700L
+#define DF_CS_UMC_AON0_DramBaseAddress0__IntLvNumChan_MASK 0x0000003CL
+#define DF_CS_UMC_AON0_DramBaseAddress0__IntLvAddrSel_MASK 0x00000E00L
#define DF_CS_UMC_AON0_DramBaseAddress0__DramBaseAddr_MASK 0xFFFFF000L
#endif
SMU11_SYSPLL3_1_ID = 6,
};
-
enum atom_smu11_syspll0_clock_id {
- SMU11_SYSPLL0_SOCCLK_ID = 0, // SOCCLK
- SMU11_SYSPLL0_MP0CLK_ID = 1, // MP0CLK
- SMU11_SYSPLL0_DCLK_ID = 2, // DCLK
- SMU11_SYSPLL0_VCLK_ID = 3, // VCLK
- SMU11_SYSPLL0_ECLK_ID = 4, // ECLK
+ SMU11_SYSPLL0_ECLK_ID = 0, // ECLK
+ SMU11_SYSPLL0_SOCCLK_ID = 1, // SOCCLK
+ SMU11_SYSPLL0_MP0CLK_ID = 2, // MP0CLK
+ SMU11_SYSPLL0_DCLK_ID = 3, // DCLK
+ SMU11_SYSPLL0_VCLK_ID = 4, // VCLK
SMU11_SYSPLL0_DCEFCLK_ID = 5, // DCEFCLK
};
-
enum atom_smu11_syspll1_0_clock_id {
SMU11_SYSPLL1_0_UCLKA_ID = 0, // UCLK_a
};
{
struct amdgpu_device *adev = handle;
struct pp_hwmgr *hwmgr = adev->powerplay.pp_handle;
- int ret;
if (hwmgr && hwmgr->pm_en) {
mutex_lock(&hwmgr->smu_lock);
if (adev->pm.smu_prv_buffer_size != 0)
pp_reserve_vram_for_smu(adev);
- if (hwmgr->hwmgr_func->gfx_off_control &&
- (hwmgr->feature_mask & PP_GFXOFF_MASK)) {
- ret = hwmgr->hwmgr_func->gfx_off_control(hwmgr, true);
- if (ret)
- pr_err("gfx off enabling failed!\n");
- }
-
return 0;
}
}
if (hwmgr->hwmgr_func->enable_per_cu_power_gating == NULL) {
- pr_info("%s was not implemented.\n", __func__);
+ pr_debug("%s was not implemented.\n", __func__);
return 0;
}
if (skip)
return 0;
- if (!hwmgr->ps)
- /*
- * for vega12/vega20 which does not support power state manager
- * DAL clock limits should also be honoured
- */
- phm_apply_clock_adjust_rules(hwmgr);
-
phm_pre_display_configuration_changed(hwmgr);
phm_display_configuration_changed(hwmgr);
if (hwmgr->ps)
power_state_management(hwmgr, new_ps);
+ else
+ /*
+ * for vega12/vega20 which does not support power state manager
+ * DAL clock limits should also be honoured
+ */
+ phm_apply_clock_adjust_rules(hwmgr);
phm_notify_smc_display_config_after_ps_adjustment(hwmgr);
uint32_t ix;
parameters.clk_id = id;
+ parameters.syspll_id = 0;
parameters.command = GET_SMU_CLOCK_INFO_V3_1_GET_CLOCK_FREQ;
+ parameters.dfsdid = 0;
ix = GetIndexIntoMasterCmdTable(getsmuclockinfo);
return -EINVAL;
output = (struct atom_get_smu_clock_info_output_parameters_v3_1 *)¶meters;
- *frequency = output->atom_smu_outputclkfreq.smu_clock_freq_hz / 10000;
+ *frequency = le32_to_cpu(output->atom_smu_outputclkfreq.smu_clock_freq_hz) / 10000;
return 0;
}
hwmgr->platform_descriptor.maxOverdriveVDDC = 0;
hwmgr->platform_descriptor.overdriveVDDCStep = 0;
- if (hwmgr->platform_descriptor.overdriveLimit.engineClock == 0 \
- || hwmgr->platform_descriptor.overdriveLimit.memoryClock == 0) {
- hwmgr->od_enabled = false;
- pr_debug("OverDrive feature not support by VBIOS\n");
- }
-
return 0;
}
powerplay_table,
(const ATOM_FIRMWARE_INFO_V2_1 *)fw_info);
- if (hwmgr->platform_descriptor.overdriveLimit.engineClock == 0
- && hwmgr->platform_descriptor.overdriveLimit.memoryClock == 0) {
- hwmgr->od_enabled = false;
- pr_debug("OverDrive feature not support by VBIOS\n");
- }
-
return result;
}
static int smu10_display_clock_voltage_request(struct pp_hwmgr *hwmgr,
- struct pp_display_clock_request *clock_req);
+ struct pp_display_clock_request *clock_req)
+{
+ struct smu10_hwmgr *smu10_data = (struct smu10_hwmgr *)(hwmgr->backend);
+ enum amd_pp_clock_type clk_type = clock_req->clock_type;
+ uint32_t clk_freq = clock_req->clock_freq_in_khz / 1000;
+ PPSMC_Msg msg;
+ switch (clk_type) {
+ case amd_pp_dcf_clock:
+ if (clk_freq == smu10_data->dcf_actual_hard_min_freq)
+ return 0;
+ msg = PPSMC_MSG_SetHardMinDcefclkByFreq;
+ smu10_data->dcf_actual_hard_min_freq = clk_freq;
+ break;
+ case amd_pp_soc_clock:
+ msg = PPSMC_MSG_SetHardMinSocclkByFreq;
+ break;
+ case amd_pp_f_clock:
+ if (clk_freq == smu10_data->f_actual_hard_min_freq)
+ return 0;
+ smu10_data->f_actual_hard_min_freq = clk_freq;
+ msg = PPSMC_MSG_SetHardMinFclkByFreq;
+ break;
+ default:
+ pr_info("[DisplayClockVoltageRequest]Invalid Clock Type!");
+ return -EINVAL;
+ }
+ smum_send_msg_to_smc_with_parameter(hwmgr, msg, clk_freq);
+
+ return 0;
+}
static struct smu10_power_state *cast_smu10_ps(struct pp_hw_power_state *hw_ps)
{
static int smu10_disable_dpm_tasks(struct pp_hwmgr *hwmgr)
{
- return smu10_disable_gfx_off(hwmgr);
+ return 0;
}
static int smu10_enable_gfx_off(struct pp_hwmgr *hwmgr)
static int smu10_enable_dpm_tasks(struct pp_hwmgr *hwmgr)
{
- return smu10_enable_gfx_off(hwmgr);
+ return 0;
}
static int smu10_gfx_off_control(struct pp_hwmgr *hwmgr, bool enable)
case amd_pp_soc_clock:
pclk_vol_table = pinfo->vdd_dep_on_socclk;
break;
+ case amd_pp_disp_clock:
+ pclk_vol_table = pinfo->vdd_dep_on_dispclk;
+ break;
+ case amd_pp_phy_clock:
+ pclk_vol_table = pinfo->vdd_dep_on_phyclk;
+ break;
default:
return -EINVAL;
}
return 0;
}
-static int smu10_display_clock_voltage_request(struct pp_hwmgr *hwmgr,
- struct pp_display_clock_request *clock_req)
-{
- struct smu10_hwmgr *smu10_data = (struct smu10_hwmgr *)(hwmgr->backend);
- enum amd_pp_clock_type clk_type = clock_req->clock_type;
- uint32_t clk_freq = clock_req->clock_freq_in_khz / 1000;
- PPSMC_Msg msg;
- switch (clk_type) {
- case amd_pp_dcf_clock:
- if (clk_freq == smu10_data->dcf_actual_hard_min_freq)
- return 0;
- msg = PPSMC_MSG_SetHardMinDcefclkByFreq;
- smu10_data->dcf_actual_hard_min_freq = clk_freq;
- break;
- case amd_pp_soc_clock:
- msg = PPSMC_MSG_SetHardMinSocclkByFreq;
- break;
- case amd_pp_f_clock:
- if (clk_freq == smu10_data->f_actual_hard_min_freq)
- return 0;
- smu10_data->f_actual_hard_min_freq = clk_freq;
- msg = PPSMC_MSG_SetHardMinFclkByFreq;
- break;
- default:
- pr_info("[DisplayClockVoltageRequest]Invalid Clock Type!");
- return -EINVAL;
- }
-
- smum_send_msg_to_smc_with_parameter(hwmgr, msg, clk_freq);
-
- return 0;
-}
static int smu10_get_max_high_clocks(struct pp_hwmgr *hwmgr, struct amd_pp_simple_clock_info *clocks)
{
.set_mmhub_powergating_by_smu = smu10_set_mmhub_powergating_by_smu,
.smus_notify_pwe = smu10_smus_notify_pwe,
.gfx_off_control = smu10_gfx_off_control,
+ .display_clock_voltage_request = smu10_display_clock_voltage_request,
};
int smu10_init_function_pointers(struct pp_hwmgr *hwmgr)
data->dpm_table.sclk_table.count++;
}
}
-
+ if (hwmgr->platform_descriptor.overdriveLimit.engineClock == 0)
+ hwmgr->platform_descriptor.overdriveLimit.engineClock = dep_sclk_table->entries[i-1].clk;
/* Initialize Mclk DPM table based on allow Mclk values */
data->dpm_table.mclk_table.count = 0;
for (i = 0; i < dep_mclk_table->count; i++) {
}
}
+ if (hwmgr->platform_descriptor.overdriveLimit.memoryClock == 0)
+ hwmgr->platform_descriptor.overdriveLimit.memoryClock = dep_mclk_table->entries[i-1].clk;
return 0;
}
static int smu7_generate_dpm_level_enable_mask(
struct pp_hwmgr *hwmgr, const void *input)
{
- int result;
+ int result = 0;
const struct phm_set_power_state_input *states =
(const struct phm_set_power_state_input *)input;
struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
const struct smu7_power_state *smu7_ps =
cast_const_phw_smu7_power_state(states->pnew_state);
- result = smu7_trim_dpm_states(hwmgr, smu7_ps);
+ /*skip the trim if od is enabled*/
+ if (!hwmgr->od_enabled)
+ result = smu7_trim_dpm_states(hwmgr, smu7_ps);
+
if (result)
return result;
odn_table->min_vddc = dep_table[0]->entries[0].vddc;
i = od_table[2]->count - 1;
- od_table[2]->entries[i].clk = hwmgr->platform_descriptor.overdriveLimit.memoryClock;
- od_table[2]->entries[i].vddc = odn_table->max_vddc;
+ od_table[2]->entries[i].clk = hwmgr->platform_descriptor.overdriveLimit.memoryClock > od_table[2]->entries[i].clk ?
+ hwmgr->platform_descriptor.overdriveLimit.memoryClock :
+ od_table[2]->entries[i].clk;
+ od_table[2]->entries[i].vddc = odn_table->max_vddc > od_table[2]->entries[i].vddc ?
+ odn_table->max_vddc :
+ od_table[2]->entries[i].vddc;
return 0;
}
vega10_setup_default_single_dpm_table(hwmgr,
dpm_table,
dep_gfx_table);
+ if (hwmgr->platform_descriptor.overdriveLimit.engineClock == 0)
+ hwmgr->platform_descriptor.overdriveLimit.engineClock =
+ dpm_table->dpm_levels[dpm_table->count-1].value;
vega10_init_dpm_state(&(dpm_table->dpm_state));
/* Initialize Mclk DPM table based on allow Mclk values */
vega10_setup_default_single_dpm_table(hwmgr,
dpm_table,
dep_mclk_table);
+ if (hwmgr->platform_descriptor.overdriveLimit.memoryClock == 0)
+ hwmgr->platform_descriptor.overdriveLimit.memoryClock =
+ dpm_table->dpm_levels[dpm_table->count-1].value;
+
vega10_init_dpm_state(&(dpm_table->dpm_state));
data->dpm_table.eclk_table.count = 0;
for (count = 0; count < num_se; count++) {
data = GRBM_GFX_INDEX__INSTANCE_BROADCAST_WRITES_MASK | GRBM_GFX_INDEX__SH_BROADCAST_WRITES_MASK | ( count << GRBM_GFX_INDEX__SE_INDEX__SHIFT);
WREG32_SOC15(GC, 0, mmGRBM_GFX_INDEX, data);
- result |= vega10_program_didt_config_registers(hwmgr, PSMSEEDCStallPatternConfig_Vega10, VEGA10_CONFIGREG_DIDT);
+ result = vega10_program_didt_config_registers(hwmgr, PSMSEEDCStallPatternConfig_Vega10, VEGA10_CONFIGREG_DIDT);
result |= vega10_program_didt_config_registers(hwmgr, PSMSEEDCStallDelayConfig_Vega10, VEGA10_CONFIGREG_DIDT);
result |= vega10_program_didt_config_registers(hwmgr, PSMSEEDCCtrlResetConfig_Vega10, VEGA10_CONFIGREG_DIDT);
result |= vega10_program_didt_config_registers(hwmgr, PSMSEEDCCtrlConfig_Vega10, VEGA10_CONFIGREG_DIDT);
hwmgr->platform_descriptor.maxOverdriveVDDC = 0;
hwmgr->platform_descriptor.overdriveVDDCStep = 0;
- if (hwmgr->platform_descriptor.overdriveLimit.engineClock == 0 ||
- hwmgr->platform_descriptor.overdriveLimit.memoryClock == 0) {
- hwmgr->od_enabled = false;
- pr_debug("OverDrive feature not support by VBIOS\n");
- }
-
return 0;
}
struct dma_fence * fence = entity->dependency;
struct drm_sched_fence *s_fence;
- if (fence->context == entity->fence_context) {
- /* We can ignore fences from ourself */
+ if (fence->context == entity->fence_context ||
+ fence->context == entity->fence_context + 1) {
+ /*
+ * Fence is a scheduled/finished fence from a job
+ * which belongs to the same entity, we can ignore
+ * fences from ourself
+ */
dma_fence_put(entity->dependency);
return false;
}
tristate "DRM Support for SH Mobile"
depends on DRM && ARM
depends on ARCH_SHMOBILE || COMPILE_TEST
- depends on FB_SH_MOBILE_MERAM || !FB_SH_MOBILE_MERAM
select BACKLIGHT_CLASS_DEVICE
select BACKLIGHT_LCD_SUPPORT
select DRM_KMS_HELPER
#include <drm/drm_gem_cma_helper.h>
#include <drm/drm_plane_helper.h>
-#include <video/sh_mobile_meram.h>
-
#include "shmob_drm_backlight.h"
#include "shmob_drm_crtc.h"
#include "shmob_drm_drv.h"
if (ret < 0)
return ret;
}
-#if 0
- if (sdev->meram_dev && sdev->meram_dev->pdev)
- pm_runtime_get_sync(&sdev->meram_dev->pdev->dev);
-#endif
return 0;
}
static void shmob_drm_clk_off(struct shmob_drm_device *sdev)
{
-#if 0
- if (sdev->meram_dev && sdev->meram_dev->pdev)
- pm_runtime_put_sync(&sdev->meram_dev->pdev->dev);
-#endif
if (sdev->clock)
clk_disable_unprepare(sdev->clock);
}
if (!scrtc->started)
return;
- /* Disable the MERAM cache. */
- if (scrtc->cache) {
- sh_mobile_meram_cache_free(sdev->meram, scrtc->cache);
- scrtc->cache = NULL;
- }
-
/* Stop the LCDC. */
shmob_drm_crtc_start_stop(scrtc, false);
{
struct drm_crtc *crtc = &scrtc->crtc;
struct drm_framebuffer *fb = crtc->primary->fb;
- struct shmob_drm_device *sdev = crtc->dev->dev_private;
struct drm_gem_cma_object *gem;
unsigned int bpp;
+ y / (bpp == 4 ? 2 : 1) * fb->pitches[1]
+ x * (bpp == 16 ? 2 : 1);
}
-
- if (scrtc->cache)
- sh_mobile_meram_cache_update(sdev->meram, scrtc->cache,
- scrtc->dma[0], scrtc->dma[1],
- &scrtc->dma[0], &scrtc->dma[1]);
}
static void shmob_drm_crtc_update_base(struct shmob_drm_crtc *scrtc)
{
struct shmob_drm_crtc *scrtc = to_shmob_crtc(crtc);
struct shmob_drm_device *sdev = crtc->dev->dev_private;
- const struct sh_mobile_meram_cfg *mdata = sdev->pdata->meram;
const struct shmob_drm_format_info *format;
- void *cache;
format = shmob_drm_format_info(crtc->primary->fb->format->format);
if (format == NULL) {
scrtc->format = format;
scrtc->line_size = crtc->primary->fb->pitches[0];
- if (sdev->meram) {
- /* Enable MERAM cache if configured. We need to de-init
- * configured ICBs before we can re-initialize them.
- */
- if (scrtc->cache) {
- sh_mobile_meram_cache_free(sdev->meram, scrtc->cache);
- scrtc->cache = NULL;
- }
-
- cache = sh_mobile_meram_cache_alloc(sdev->meram, mdata,
- crtc->primary->fb->pitches[0],
- adjusted_mode->vdisplay,
- format->meram,
- &scrtc->line_size);
- if (!IS_ERR(cache))
- scrtc->cache = cache;
- }
-
shmob_drm_crtc_compute_base(scrtc, x, y);
return 0;
int dpms;
const struct shmob_drm_format_info *format;
- void *cache;
unsigned long dma[2];
unsigned int line_size;
bool started;
struct clk;
struct device;
struct drm_device;
-struct sh_mobile_meram_info;
struct shmob_drm_device {
struct device *dev;
void __iomem *mmio;
struct clk *clock;
- struct sh_mobile_meram_info *meram;
u32 lddckr;
u32 ldmt1r;
#include <drm/drm_gem_cma_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
-#include <video/sh_mobile_meram.h>
-
#include "shmob_drm_crtc.h"
#include "shmob_drm_drv.h"
#include "shmob_drm_kms.h"
.bpp = 16,
.yuv = false,
.lddfr = LDDFR_PKF_RGB16,
- .meram = SH_MOBILE_MERAM_PF_RGB,
}, {
.fourcc = DRM_FORMAT_RGB888,
.bpp = 24,
.yuv = false,
.lddfr = LDDFR_PKF_RGB24,
- .meram = SH_MOBILE_MERAM_PF_RGB,
}, {
.fourcc = DRM_FORMAT_ARGB8888,
.bpp = 32,
.yuv = false,
.lddfr = LDDFR_PKF_ARGB32,
- .meram = SH_MOBILE_MERAM_PF_RGB,
}, {
.fourcc = DRM_FORMAT_NV12,
.bpp = 12,
.yuv = true,
.lddfr = LDDFR_CC | LDDFR_YF_420,
- .meram = SH_MOBILE_MERAM_PF_NV,
}, {
.fourcc = DRM_FORMAT_NV21,
.bpp = 12,
.yuv = true,
.lddfr = LDDFR_CC | LDDFR_YF_420,
- .meram = SH_MOBILE_MERAM_PF_NV,
}, {
.fourcc = DRM_FORMAT_NV16,
.bpp = 16,
.yuv = true,
.lddfr = LDDFR_CC | LDDFR_YF_422,
- .meram = SH_MOBILE_MERAM_PF_NV,
}, {
.fourcc = DRM_FORMAT_NV61,
.bpp = 16,
.yuv = true,
.lddfr = LDDFR_CC | LDDFR_YF_422,
- .meram = SH_MOBILE_MERAM_PF_NV,
}, {
.fourcc = DRM_FORMAT_NV24,
.bpp = 24,
.yuv = true,
.lddfr = LDDFR_CC | LDDFR_YF_444,
- .meram = SH_MOBILE_MERAM_PF_NV24,
}, {
.fourcc = DRM_FORMAT_NV42,
.bpp = 24,
.yuv = true,
.lddfr = LDDFR_CC | LDDFR_YF_444,
- .meram = SH_MOBILE_MERAM_PF_NV24,
},
};
unsigned int bpp;
bool yuv;
u32 lddfr;
- unsigned int meram;
};
const struct shmob_drm_format_info *shmob_drm_format_info(u32 fourcc);
#include <drm/drm_fb_cma_helper.h>
#include <drm/drm_gem_cma_helper.h>
-#include <video/sh_mobile_meram.h>
-
#include "shmob_drm_drv.h"
#include "shmob_drm_kms.h"
#include "shmob_drm_plane.h"
Some "legacy" VGA devices implemented on PCI typically have the same
hard-decoded addresses as they did on ISA. When multiple PCI devices
are accessed at same time they need some kind of coordination. Please
- see Documentation/vgaarbiter.txt for more details. Select this to
+ see Documentation/gpu/vgaarbiter.rst for more details. Select this to
enable VGA arbiter.
config VGA_ARB_MAX_GPUS
/*
* vgaarb.c: Implements the VGA arbitration. For details refer to
- * Documentation/vgaarbiter.txt
+ * Documentation/gpu/vgaarbiter.rst
*
*
* (C) Copyright 2005 Benjamin Herrenschmidt <benh@kernel.crashing.org>
You can't use this driver and the HIDBP (Boot Protocol) keyboard
and mouse drivers at the same time. More information is available:
- <file:Documentation/input/input.txt>.
+ <file:Documentation/input/input.rst>.
If unsure, say Y.
}
}
} else { /* normal 7bit address */
- addr = msg->addr << 1;
- if (flags & I2C_M_RD)
- addr |= 1;
+ addr = i2c_8bit_addr_from_msg(msg);
if (flags & I2C_M_REV_DIR_ADDR)
addr ^= 1;
ret = try_address(i2c_adap, addr, retries);
struct i2c_msg *msg)
{
int sta = pca_get_con(adap);
- int addr;
+ int addr = i2c_8bit_addr_from_msg(msg);
- addr = ((0x7f & msg->addr) << 1);
- if (msg->flags & I2C_M_RD)
- addr |= 1;
DEB2("=== SLAVE ADDRESS %#04x+%c=%#04x\n",
msg->addr, msg->flags & I2C_M_RD ? 'R' : 'W', addr);
static int pcf_doAddress(struct i2c_algo_pcf_data *adap,
struct i2c_msg *msg)
{
- unsigned short flags = msg->flags;
- unsigned char addr;
+ unsigned char addr = i2c_8bit_addr_from_msg(msg);
- addr = msg->addr << 1;
- if (flags & I2C_M_RD)
- addr |= 1;
- if (flags & I2C_M_REV_DIR_ADDR)
+ if (msg->flags & I2C_M_REV_DIR_ADDR)
addr ^= 1;
i2c_outb(adap, addr);
config I2C_STM32F7
tristate "STMicroelectronics STM32F7 I2C support"
depends on ARCH_STM32 || COMPILE_TEST
+ select I2C_SLAVE
help
Enable this option to add support for STM32 I2C controller embedded
in STM32F7 SoCs.
obj-$(CONFIG_I2C_SPRD) += i2c-sprd.o
obj-$(CONFIG_I2C_ST) += i2c-st.o
obj-$(CONFIG_I2C_STM32F4) += i2c-stm32f4.o
-obj-$(CONFIG_I2C_STM32F7) += i2c-stm32f7.o
+i2c-stm32f7-drv-objs := i2c-stm32f7.o i2c-stm32.o
+obj-$(CONFIG_I2C_STM32F7) += i2c-stm32f7-drv.o
obj-$(CONFIG_I2C_STU300) += i2c-stu300.o
obj-$(CONFIG_I2C_SUN6I_P2WI) += i2c-sun6i-p2wi.o
obj-$(CONFIG_I2C_SYNQUACER) += i2c-synquacer.o
{
u32 command = ASPEED_I2CD_M_START_CMD | ASPEED_I2CD_M_TX_CMD;
struct i2c_msg *msg = &bus->msgs[bus->msgs_index];
- u8 slave_addr = msg->addr << 1;
+ u8 slave_addr = i2c_8bit_addr_from_msg(msg);
bus->master_state = ASPEED_I2C_MASTER_START;
bus->buf_index = 0;
if (msg->flags & I2C_M_RD) {
- slave_addr |= 1;
command |= ASPEED_I2CD_M_RX_CMD;
/* Need to let the hardware know to NACK after RX. */
if (msg->len == 1 && !(msg->flags & I2C_M_RECV_LEN))
* the RXRDY interrupt first in order to not keep garbage data in the
* Receive Holding Register for the next transfer.
*/
- if (irqstatus & AT91_TWI_RXRDY)
- at91_twi_read_next_byte(dev);
+ if (irqstatus & AT91_TWI_RXRDY) {
+ /*
+ * Read all available bytes at once by polling RXRDY usable w/
+ * and w/o FIFO. With FIFO enabled we could also read RXFL and
+ * avoid polling RXRDY.
+ */
+ do {
+ at91_twi_read_next_byte(dev);
+ } while (at91_twi_read(dev, AT91_TWI_SR) & AT91_TWI_RXRDY);
+ }
/*
* When a NACK condition is detected, the I2C controller sets the NACK,
* addr_2: addr[7:0]
*/
addr_1 = 0xF0 | ((msg->addr >> 7) & 0x06);
+ if (i2c_m_rd(msg))
+ addr_1 |= 1; /* Set the R/nW bit of the address */
addr_2 = msg->addr & 0xFF;
} else {
/* 7-bit address
* addr_1: addr[6:0] | (R/nW)
* addr_2: dont care
*/
- addr_1 = (msg->addr << 1) & 0xFF;
+ addr_1 = i2c_8bit_addr_from_msg(msg);
addr_2 = 0;
}
/* I2C read transfer */
rx_xfer = i2c_m_recv_len(msg) ? I2C_SMBUS_BLOCK_MAX : msg->len;
tx_xfer = 0;
- addr_1 |= 1; /* Set the R/nW bit of the address */
} else {
/* I2C write transfer */
rx_xfer = 0;
if (idev->bus_clk_rate == 0)
idev->bus_clk_rate = 100000; /* default clock rate */
+ ret = clk_prepare_enable(idev->i2c_clk);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to enable clock\n");
+ return ret;
+ }
+
ret = axxia_i2c_init(idev);
if (ret) {
dev_err(&pdev->dev, "failed to initialize\n");
- return ret;
+ goto error_disable_clk;
}
ret = devm_request_irq(&pdev->dev, irq, axxia_i2c_isr, 0,
pdev->name, idev);
if (ret) {
dev_err(&pdev->dev, "failed to claim IRQ%d\n", irq);
- return ret;
- }
-
- ret = clk_prepare_enable(idev->i2c_clk);
- if (ret) {
- dev_err(&pdev->dev, "failed to enable clock\n");
- return ret;
+ goto error_disable_clk;
}
i2c_set_adapdata(&idev->adapter, idev);
platform_set_drvdata(pdev, idev);
ret = i2c_add_adapter(&idev->adapter);
- if (ret) {
- clk_disable_unprepare(idev->i2c_clk);
- return ret;
- }
+ if (ret)
+ goto error_disable_clk;
return 0;
+
+error_disable_clk:
+ clk_disable_unprepare(idev->i2c_clk);
+ return ret;
}
static int axxia_i2c_remove(struct platform_device *pdev)
return ((ic_clk * (tLOW + tf) + 500000) / 1000000) - 1 + offset;
}
-void __i2c_dw_enable(struct dw_i2c_dev *dev, bool enable)
-{
- dw_writel(dev, enable, DW_IC_ENABLE);
-}
-
-void __i2c_dw_enable_and_wait(struct dw_i2c_dev *dev, bool enable)
+void __i2c_dw_disable(struct dw_i2c_dev *dev)
{
int timeout = 100;
do {
- __i2c_dw_enable(dev, enable);
- if ((dw_readl(dev, DW_IC_ENABLE_STATUS) & 1) == enable)
+ __i2c_dw_disable_nowait(dev);
+ /*
+ * The enable status register may be unimplemented, but
+ * in that case this test reads zero and exits the loop.
+ */
+ if ((dw_readl(dev, DW_IC_ENABLE_STATUS) & 1) == 0)
return;
/*
usleep_range(25, 250);
} while (timeout--);
- dev_warn(dev->dev, "timeout in %sabling adapter\n",
- enable ? "en" : "dis");
+ dev_warn(dev->dev, "timeout in disabling adapter\n");
}
unsigned long i2c_dw_clk_rate(struct dw_i2c_dev *dev)
void i2c_dw_disable(struct dw_i2c_dev *dev)
{
/* Disable controller */
- __i2c_dw_enable_and_wait(dev, false);
+ __i2c_dw_disable(dev);
/* Disable all interupts */
dw_writel(dev, 0, DW_IC_INTR_MASK);
void dw_writel(struct dw_i2c_dev *dev, u32 b, int offset);
u32 i2c_dw_scl_hcnt(u32 ic_clk, u32 tSYMBOL, u32 tf, int cond, int offset);
u32 i2c_dw_scl_lcnt(u32 ic_clk, u32 tLOW, u32 tf, int offset);
-void __i2c_dw_enable(struct dw_i2c_dev *dev, bool enable);
-void __i2c_dw_enable_and_wait(struct dw_i2c_dev *dev, bool enable);
unsigned long i2c_dw_clk_rate(struct dw_i2c_dev *dev);
int i2c_dw_prepare_clk(struct dw_i2c_dev *dev, bool prepare);
int i2c_dw_acquire_lock(struct dw_i2c_dev *dev);
void i2c_dw_disable(struct dw_i2c_dev *dev);
void i2c_dw_disable_int(struct dw_i2c_dev *dev);
+static inline void __i2c_dw_enable(struct dw_i2c_dev *dev)
+{
+ dw_writel(dev, 1, DW_IC_ENABLE);
+}
+
+static inline void __i2c_dw_disable_nowait(struct dw_i2c_dev *dev)
+{
+ dw_writel(dev, 0, DW_IC_ENABLE);
+}
+
+void __i2c_dw_disable(struct dw_i2c_dev *dev);
+
extern u32 i2c_dw_read_comp_param(struct dw_i2c_dev *dev);
extern int i2c_dw_probe(struct dw_i2c_dev *dev);
#if IS_ENABLED(CONFIG_I2C_DESIGNWARE_SLAVE)
comp_param1 = dw_readl(dev, DW_IC_COMP_PARAM_1);
/* Disable the adapter */
- __i2c_dw_enable_and_wait(dev, false);
+ __i2c_dw_disable(dev);
/* Set standard and fast speed deviders for high/low periods */
u32 ic_con, ic_tar = 0;
/* Disable the adapter */
- __i2c_dw_enable_and_wait(dev, false);
+ __i2c_dw_disable(dev);
/* If the slave address is ten bit address, enable 10BITADDR */
ic_con = dw_readl(dev, DW_IC_CON);
i2c_dw_disable_int(dev);
/* Enable the adapter */
- __i2c_dw_enable(dev, true);
+ __i2c_dw_enable(dev);
/* Dummy read to avoid the register getting stuck on Bay Trail */
dw_readl(dev, DW_IC_ENABLE_STATUS);
* additional interrupts are a hardware bug or this driver doesn't
* handle them correctly yet.
*/
- __i2c_dw_enable(dev, false);
+ __i2c_dw_disable_nowait(dev);
if (dev->msg_err) {
ret = dev->msg_err;
comp_param1 = dw_readl(dev, DW_IC_COMP_PARAM_1);
/* Disable the adapter. */
- __i2c_dw_enable_and_wait(dev, false);
+ __i2c_dw_disable(dev);
/* Configure SDA Hold Time if required. */
reg = dw_readl(dev, DW_IC_COMP_VERSION);
* Set slave address in the IC_SAR register,
* the address to which the DW_apb_i2c responds.
*/
- __i2c_dw_enable(dev, false);
+ __i2c_dw_disable_nowait(dev);
dw_writel(dev, slave->addr, DW_IC_SAR);
dev->slave = slave;
- __i2c_dw_enable(dev, true);
+ __i2c_dw_enable(dev);
dev->cmd_err = 0;
dev->msg_write_idx = 0;
if (ret < 0)
goto abort;
}
+ ret = diolan_i2c_put_byte_ack(dev,
+ i2c_8bit_addr_from_msg(pmsg));
+ if (ret < 0)
+ goto abort;
if (pmsg->flags & I2C_M_RD) {
- ret =
- diolan_i2c_put_byte_ack(dev, (pmsg->addr << 1) | 1);
- if (ret < 0)
- goto abort;
for (j = 0; j < pmsg->len; j++) {
u8 byte;
bool ack = j < pmsg->len - 1;
pmsg->buf[j] = byte;
}
} else {
- ret = diolan_i2c_put_byte_ack(dev, pmsg->addr << 1);
- if (ret < 0)
- goto abort;
for (j = 0; j < pmsg->len; j++) {
ret = diolan_i2c_put_byte_ack(dev,
pmsg->buf[j]);
struct i2c_msg *cur_msg = &ddata->msgs[ddata->current_msg];
efm32_i2c_write32(ddata, REG_CMD, REG_CMD_START);
- efm32_i2c_write32(ddata, REG_TXDATA, cur_msg->addr << 1 |
- (cur_msg->flags & I2C_M_RD ? 1 : 0));
+ efm32_i2c_write32(ddata, REG_TXDATA, i2c_8bit_addr_from_msg(cur_msg));
}
static void efm32_i2c_send_next_byte(struct efm32_i2c_ddata *ddata)
iowrite32(addr_8_lsb, p + PCH_I2CDR);
} else {
/* set 7 bit slave address and R/W bit as 0 */
- iowrite32(addr << 1, p + PCH_I2CDR);
+ iowrite32(i2c_8bit_addr_from_msg(msgs), p + PCH_I2CDR);
if (first)
pch_i2c_start(adap);
}
iowrite32(addr_2_msb | TEN_BIT_ADDR_MASK, p + PCH_I2CDR);
} else {
/* 7 address bits + R/W bit */
- addr = (((addr) << 1) | (I2C_RD));
- iowrite32(addr, p + PCH_I2CDR);
+ iowrite32(i2c_8bit_addr_from_msg(msgs), p + PCH_I2CDR);
}
/* check if it is the first message */
em_clear_set_bit(priv, 0, I2C_BIT_STT0, I2C_OFS_IICC0);
/* Send slave address and R/W type */
- writeb((msg->addr << 1) | read, priv->base + I2C_OFS_IIC0);
+ writeb(i2c_8bit_addr_from_msg(msg), priv->base + I2C_OFS_IIC0);
/* Wait for transaction */
status = em_i2c_wait_for_event(priv);
struct i2c_msg *msgs, int num)
{
struct exynos5_i2c *i2c = adap->algo_data;
- int i = 0, ret = 0, stop = 0;
+ int i, ret;
if (i2c->suspended) {
dev_err(i2c->dev, "HS-I2C is not initialized.\n");
if (ret)
return ret;
- for (i = 0; i < num; i++, msgs++) {
- stop = (i == num - 1);
-
- ret = exynos5_i2c_xfer_msg(i2c, msgs, stop);
-
- if (ret < 0)
- goto out;
- }
-
- if (i == num) {
- ret = num;
- } else {
- /* Only one message, cannot access the device */
- if (i == 1)
- ret = -EREMOTEIO;
- else
- ret = i;
-
- dev_warn(i2c->dev, "xfer message failed\n");
+ for (i = 0; i < num; ++i) {
+ ret = exynos5_i2c_xfer_msg(i2c, msgs + i, i + 1 == num);
+ if (ret)
+ break;
}
- out:
clk_disable(i2c->clk);
- return ret;
+
+ return ret ?: num;
}
static u32 exynos5_i2c_func(struct i2c_adapter *adap)
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/i2c-algo-bit.h>
-#include <linux/i2c-gpio.h>
+#include <linux/platform_data/i2c-gpio.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#define I2C_OVER_INTR BIT(0)
#define HIX5I2C_MAX_FREQ 400000 /* 400k */
-#define HIX5I2C_READ_OPERATION 0x01
enum hix5hd2_i2c_state {
HIX5I2C_STAT_RW_ERR = -1,
hix5hd2_i2c_clr_all_irq(priv);
hix5hd2_i2c_enable_irq(priv);
- if (priv->msg->flags & I2C_M_RD)
- writel_relaxed((priv->msg->addr << 1) | HIX5I2C_READ_OPERATION,
- priv->regs + HIX5I2C_TXR);
- else
- writel_relaxed(priv->msg->addr << 1,
- priv->regs + HIX5I2C_TXR);
+ writel_relaxed(i2c_8bit_addr_from_msg(priv->msg),
+ priv->regs + HIX5I2C_TXR);
writel_relaxed(I2C_WRITE | I2C_START, priv->regs + HIX5I2C_COM);
spin_unlock_irqrestore(&priv->lock, flags);
goto out;
}
- if (i == num) {
- ret = num;
- } else {
- /* Only one message, cannot access the device */
- if (i == 1)
- ret = -EREMOTEIO;
- else
- ret = i;
-
- dev_warn(priv->dev, "xfer message failed\n");
- }
+ ret = num;
out:
pm_runtime_mark_last_busy(priv->dev);
goto err_clk;
}
- pm_suspend_ignore_children(&pdev->dev, true);
pm_runtime_set_autosuspend_delay(priv->dev, MSEC_PER_SEC);
pm_runtime_use_autosuspend(priv->dev);
pm_runtime_set_active(priv->dev);
#if IS_ENABLED(CONFIG_I2C_MUX_GPIO) && defined CONFIG_DMI
#include <linux/gpio.h>
-#include <linux/i2c-mux-gpio.h>
+#include <linux/platform_data/i2c-mux-gpio.h>
#endif
/* I801 SMBus address offsets */
pci_write_config_byte(dev, SMBHSTCFG, priv->original_hstcfg);
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int i801_suspend(struct device *dev)
{
struct pci_dev *pci_dev = to_pci_dev(dev);
}
#endif
-static UNIVERSAL_DEV_PM_OPS(i801_pm_ops, i801_suspend,
- i801_resume, NULL);
+static SIMPLE_DEV_PM_OPS(i801_pm_ops, i801_suspend, i801_resume);
static struct pci_driver i801_driver = {
.name = "i801_smbus",
DBG2("%d: iic_xfer, %d msg(s)\n", dev->idx, num);
- if (!num)
- return 0;
-
/* Check the sanity of the passed messages.
* Uhh, generic i2c layer is more suitable place for such code...
*/
+// SPDX-License-Identifier: GPL-2.0+
/*
* This is i.MX low power i2c controller driver.
*
* Copyright 2016 Freescale Semiconductor, Inc.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
*/
#include <linux/clk.h>
struct i2c_msg *msgs)
{
unsigned int temp;
- u8 read;
temp = readl(lpi2c_imx->base + LPI2C_MCR);
temp |= MCR_RRF | MCR_RTF;
writel(temp, lpi2c_imx->base + LPI2C_MCR);
writel(0x7f00, lpi2c_imx->base + LPI2C_MSR);
- read = msgs->flags & I2C_M_RD;
- temp = (msgs->addr << 1 | read) | (GEN_START << 8);
+ temp = i2c_8bit_addr_from_msg(msgs) | (GEN_START << 8);
writel(temp, lpi2c_imx->base + LPI2C_MTDR);
return lpi2c_imx_bus_busy(lpi2c_imx);
+// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2002 Motorola GSG-China
*
- * 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.
- *
* Author:
* Darius Augulis, Teltonika Inc.
*
* Write slave address.
* The first byte must be transmitted by the CPU.
*/
- imx_i2c_write_reg(msgs->addr << 1, i2c_imx, IMX_I2C_I2DR);
+ imx_i2c_write_reg(i2c_8bit_addr_from_msg(msgs), i2c_imx, IMX_I2C_I2DR);
reinit_completion(&i2c_imx->dma->cmd_complete);
time_left = wait_for_completion_timeout(
&i2c_imx->dma->cmd_complete,
int i, result;
dev_dbg(&i2c_imx->adapter.dev, "<%s> write slave address: addr=0x%x\n",
- __func__, msgs->addr << 1);
+ __func__, i2c_8bit_addr_from_msg(msgs));
/* write slave address */
- imx_i2c_write_reg(msgs->addr << 1, i2c_imx, IMX_I2C_I2DR);
+ imx_i2c_write_reg(i2c_8bit_addr_from_msg(msgs), i2c_imx, IMX_I2C_I2DR);
result = i2c_imx_trx_complete(i2c_imx);
if (result)
return result;
dev_dbg(&i2c_imx->adapter.dev,
"<%s> write slave address: addr=0x%x\n",
- __func__, (msgs->addr << 1) | 0x01);
+ __func__, i2c_8bit_addr_from_msg(msgs));
/* write slave address */
- imx_i2c_write_reg((msgs->addr << 1) | 0x01, i2c_imx, IMX_I2C_I2DR);
+ imx_i2c_write_reg(i2c_8bit_addr_from_msg(msgs), i2c_imx, IMX_I2C_I2DR);
result = i2c_imx_trx_complete(i2c_imx);
if (result)
return result;
/* 10 bit address? */
if (i2c->msg->flags & I2C_M_TEN) {
addr = 0xf0 | ((i2c->msg->addr >> 7) & 0x6);
+ /* Set read bit if necessary */
+ addr |= (i2c->msg->flags & I2C_M_RD) ? 1 : 0;
i2c->state = STATE_ADDR10;
} else {
- addr = (i2c->msg->addr << 1);
+ addr = i2c_8bit_addr_from_msg(i2c->msg);
i2c->state = STATE_START;
}
- /* Set read bit if necessary */
- addr |= (i2c->msg->flags & I2C_M_RD) ? 1 : 0;
-
kempld_write8(pld, KEMPLD_I2C_DATA, addr);
kempld_write8(pld, KEMPLD_I2C_CMD, I2C_CMD_START);
#define MLXCPLD_I2C_VALID_FLAG (I2C_M_RECV_LEN | I2C_M_RD)
#define MLXCPLD_I2C_BUS_NUM 1
#define MLXCPLD_I2C_DATA_REG_SZ 36
+#define MLXCPLD_I2C_DATA_SZ_BIT BIT(5)
+#define MLXCPLD_I2C_DATA_SZ_MASK GENMASK(6, 5)
+#define MLXCPLD_I2C_SMBUS_BLK_BIT BIT(7)
#define MLXCPLD_I2C_MAX_ADDR_LEN 4
#define MLXCPLD_I2C_RETR_NUM 2
#define MLXCPLD_I2C_XFER_TO 500000 /* usec */
#define MLXCPLD_I2C_POLL_TIME 2000 /* usec */
/* LPC I2C registers */
-#define MLXCPLD_LPCI2C_LPF_REG 0x0
+#define MLXCPLD_LPCI2C_CPBLTY_REG 0x0
#define MLXCPLD_LPCI2C_CTRL_REG 0x1
#define MLXCPLD_LPCI2C_HALF_CYC_REG 0x4
#define MLXCPLD_LPCI2C_I2C_HOLD_REG 0x5
struct mutex lock;
struct mlxcpld_i2c_curr_xfer xfer;
struct device *dev;
+ bool smbus_block;
};
static void mlxcpld_i2c_lpc_write_buf(u8 *data, u8 len, u32 addr)
* All upper layers currently are never use transfer with more than
* 2 messages. Actually, it's also not so relevant in Mellanox systems
* because of HW limitation. Max size of transfer is not more than 32
- * bytes in the current x86 LPCI2C bridge.
+ * or 68 bytes in the current x86 LPCI2C bridge.
*/
priv->xfer.cmd = msgs[num - 1].flags & I2C_M_RD;
static int mlxcpld_i2c_wait_for_tc(struct mlxcpld_i2c_priv *priv)
{
int status, i, timeout = 0;
- u8 datalen;
+ u8 datalen, val;
do {
usleep_range(MLXCPLD_I2C_POLL_TIME / 2, MLXCPLD_I2C_POLL_TIME);
* Actual read data len will be always the same as
* requested len. 0xff (line pull-up) will be returned
* if slave has no data to return. Thus don't read
- * MLXCPLD_LPCI2C_NUM_DAT_REG reg from CPLD.
+ * MLXCPLD_LPCI2C_NUM_DAT_REG reg from CPLD. Only in case of
+ * SMBus block read transaction data len can be different,
+ * check this case.
*/
- datalen = priv->xfer.data_len;
+ mlxcpld_i2c_read_comm(priv, MLXCPLD_LPCI2C_NUM_ADDR_REG, &val,
+ 1);
+ if (priv->smbus_block && (val & MLXCPLD_I2C_SMBUS_BLK_BIT)) {
+ mlxcpld_i2c_read_comm(priv, MLXCPLD_LPCI2C_NUM_DAT_REG,
+ &datalen, 1);
+ if (unlikely(datalen > (I2C_SMBUS_BLOCK_MAX + 1))) {
+ dev_err(priv->dev, "Incorrect smbus block read message len\n");
+ return -E2BIG;
+ }
+ } else {
+ datalen = priv->xfer.data_len;
+ }
mlxcpld_i2c_read_comm(priv, MLXCPLD_LPCI2C_DATA_REG,
priv->xfer.msg[i].buf, datalen);
static void mlxcpld_i2c_xfer_msg(struct mlxcpld_i2c_priv *priv)
{
int i, len = 0;
- u8 cmd;
+ u8 cmd, val;
mlxcpld_i2c_write_comm(priv, MLXCPLD_LPCI2C_NUM_DAT_REG,
&priv->xfer.data_len, 1);
- mlxcpld_i2c_write_comm(priv, MLXCPLD_LPCI2C_NUM_ADDR_REG,
- &priv->xfer.addr_width, 1);
+
+ val = priv->xfer.addr_width;
+ /* Notify HW about SMBus block read transaction */
+ if (priv->smbus_block && priv->xfer.msg_num >= 2 &&
+ priv->xfer.msg[1].len == 1 &&
+ (priv->xfer.msg[1].flags & I2C_M_RECV_LEN) &&
+ (priv->xfer.msg[1].flags & I2C_M_RD))
+ val |= MLXCPLD_I2C_SMBUS_BLK_BIT;
+
+ mlxcpld_i2c_write_comm(priv, MLXCPLD_LPCI2C_NUM_ADDR_REG, &val, 1);
for (i = 0; i < priv->xfer.msg_num; i++) {
if ((priv->xfer.msg[i].flags & I2C_M_RD) != I2C_M_RD) {
static u32 mlxcpld_i2c_func(struct i2c_adapter *adap)
{
- return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SMBUS_BLOCK_DATA;
+ struct mlxcpld_i2c_priv *priv = i2c_get_adapdata(adap);
+
+ if (priv->smbus_block)
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL |
+ I2C_FUNC_SMBUS_I2C_BLOCK | I2C_FUNC_SMBUS_BLOCK_DATA;
+ else
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL |
+ I2C_FUNC_SMBUS_I2C_BLOCK;
}
static const struct i2c_algorithm mlxcpld_i2c_algo = {
.max_comb_1st_msg_len = 4,
};
+static const struct i2c_adapter_quirks mlxcpld_i2c_quirks_ext = {
+ .flags = I2C_AQ_COMB_WRITE_THEN_READ,
+ .max_read_len = MLXCPLD_I2C_DATA_REG_SZ * 2 - MLXCPLD_I2C_MAX_ADDR_LEN,
+ .max_write_len = MLXCPLD_I2C_DATA_REG_SZ * 2,
+ .max_comb_1st_msg_len = 4,
+};
+
static struct i2c_adapter mlxcpld_i2c_adapter = {
.owner = THIS_MODULE,
.name = "i2c-mlxcpld",
{
struct mlxcpld_i2c_priv *priv;
int err;
+ u8 val;
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
/* Register with i2c layer */
mlxcpld_i2c_adapter.timeout = usecs_to_jiffies(MLXCPLD_I2C_XFER_TO);
+ /* Read capability register */
+ mlxcpld_i2c_read_comm(priv, MLXCPLD_LPCI2C_CPBLTY_REG, &val, 1);
+ /* Check support for extended transaction length */
+ if ((val & MLXCPLD_I2C_DATA_SZ_MASK) == MLXCPLD_I2C_DATA_SZ_BIT)
+ mlxcpld_i2c_adapter.quirks = &mlxcpld_i2c_quirks_ext;
+ /* Check support for smbus block transaction */
+ if (val & MLXCPLD_I2C_SMBUS_BLK_BIT)
+ priv->smbus_block = true;
+ if (pdev->id >= -1)
+ mlxcpld_i2c_adapter.nr = pdev->id;
priv->adap = mlxcpld_i2c_adapter;
priv->adap.dev.parent = &pdev->dev;
priv->base_addr = MLXPLAT_CPLD_LPC_I2C_BASE_ADDR;
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/of_address.h>
+#include <linux/of_device.h>
#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include <linux/scatterlist.h>
static int mtk_i2c_probe(struct platform_device *pdev)
{
- const struct of_device_id *of_id;
int ret = 0;
struct mtk_i2c *i2c;
struct clk *clk;
init_completion(&i2c->msg_complete);
- of_id = of_match_node(mtk_i2c_of_match, pdev->dev.of_node);
- if (!of_id)
- return -EINVAL;
-
- i2c->dev_comp = of_id->data;
+ i2c->dev_comp = of_device_get_match_data(&pdev->dev);
i2c->adap.dev.of_node = pdev->dev.of_node;
i2c->dev = &pdev->dev;
i2c->adap.dev.parent = &pdev->dev;
+// SPDX-License-Identifier: GPL-2.0+
/*
* Freescale MXS I2C bus driver
*
* based on a (non-working) driver which was:
*
* Copyright (C) 2009-2010 Freescale Semiconductor, Inc. All Rights Reserved.
- *
- * 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.
- *
*/
#include <linux/slab.h>
struct dma_async_tx_descriptor *desc;
struct mxs_i2c_dev *i2c = i2c_get_adapdata(adap);
+ i2c->addr_data = i2c_8bit_addr_from_msg(msg);
+
if (msg->flags & I2C_M_RD) {
i2c->dma_read = true;
- i2c->addr_data = (msg->addr << 1) | I2C_SMBUS_READ;
/*
* SELECT command.
}
} else {
i2c->dma_read = false;
- i2c->addr_data = (msg->addr << 1) | I2C_SMBUS_WRITE;
/*
* WRITE command.
struct i2c_msg *msg, uint32_t flags)
{
struct mxs_i2c_dev *i2c = i2c_get_adapdata(adap);
- uint32_t addr_data = msg->addr << 1;
+ uint32_t addr_data = i2c_8bit_addr_from_msg(msg);
uint32_t data = 0;
int i, ret, xlen = 0, xmit = 0;
uint32_t start;
*/
BUG_ON(msg->len > 4);
- addr_data |= I2C_SMBUS_READ;
-
/* SELECT command. */
mxs_i2c_pio_trigger_write_cmd(i2c, MXS_CMD_I2C_SELECT,
addr_data);
* fast enough. It is possible to transfer arbitrary amount
* of data using PIO write.
*/
- addr_data |= I2C_SMBUS_WRITE;
/*
* The LSB of data buffer is the first byte blasted across
goto err_no_mem;
}
- pm_suspend_ignore_children(&adev->dev, true);
-
dev->clk = devm_clk_get(&adev->dev, NULL);
if (IS_ERR(dev->clk)) {
dev_err(&adev->dev, "could not get i2c clock\n");
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/wait.h>
-#include <linux/i2c-ocores.h>
+#include <linux/platform_data/i2c-ocores.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/log2.h>
i2c->nmsgs = num;
i2c->state = STATE_START;
- oc_setreg(i2c, OCI2C_DATA,
- (i2c->msg->addr << 1) |
- ((i2c->msg->flags & I2C_M_RD) ? 1:0));
-
+ oc_setreg(i2c, OCI2C_DATA, i2c_8bit_addr_from_msg(i2c->msg));
oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_START);
if (wait_event_timeout(i2c->wait, (i2c->state == STATE_ERROR) ||
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/slab.h>
-#include <linux/i2c-omap.h>
+#include <linux/platform_data/i2c-omap.h>
#include <linux/pm_runtime.h>
#include <linux/pinctrl/consumer.h>
*/
memset(&req, 0, sizeof(req));
switch(num) {
- case 0:
- return 0;
case 1:
req.type = (msgs[0].flags & I2C_M_RD) ?
OPAL_I2C_RAW_READ : OPAL_I2C_RAW_WRITE;
req.size = cpu_to_be32(msgs[1].len);
req.buffer_ra = cpu_to_be64(__pa(msgs[1].buf));
break;
- default:
- return -EOPNOTSUPP;
}
rc = i2c_opal_send_request(opal_id, &req);
read = msg->flags & I2C_M_RD ? 1 : 0;
- TXFIFO_WR(smbus, MTXFIFO_START | (msg->addr << 1) | read);
+ TXFIFO_WR(smbus, MTXFIFO_START | i2c_8bit_addr_from_msg(msg));
if (read) {
TXFIFO_WR(smbus, msg->len | MTXFIFO_READ |
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/i2c-algo-pca.h>
-#include <linux/i2c-pca-platform.h>
+#include <linux/platform_data/i2c-pca-platform.h>
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/io.h>
#include <linux/timer.h>
#include <linux/completion.h>
#include <linux/platform_device.h>
-#include <linux/i2c-pnx.h>
#include <linux/io.h>
#include <linux/err.h>
#include <linux/clk.h>
#define I2C_PNX_SPEED_KHZ_DEFAULT 100
#define I2C_PNX_REGION_SIZE 0x100
+struct i2c_pnx_mif {
+ int ret; /* Return value */
+ int mode; /* Interface mode */
+ struct completion complete; /* I/O completion */
+ struct timer_list timer; /* Timeout */
+ u8 * buf; /* Data buffer */
+ int len; /* Length of data buffer */
+ int order; /* RX Bytes to order via TX */
+};
+
+struct i2c_pnx_algo_data {
+ void __iomem *ioaddr;
+ struct i2c_pnx_mif mif;
+ int last;
+ struct clk *clk;
+ struct i2c_adapter adapter;
+ int irq;
+ u32 timeout;
+};
+
enum {
mstatus_tdi = 0x00000001,
mstatus_afi = 0x00000002,
*/
#define TOUT_MIN 2
+/* I2C Frequency Modes */
+#define I2C_STANDARD_FREQ 100000
+#define I2C_FAST_MODE_FREQ 400000
+#define I2C_FAST_MODE_PLUS_FREQ 1000000
+
/* Default values. Use these if FW query fails */
-#define DEFAULT_CLK_FREQ 100000
+#define DEFAULT_CLK_FREQ I2C_STANDARD_FREQ
#define DEFAULT_SRC_CLK 20000000
/*
/* TAG length for DATA READ in RX FIFO */
#define READ_RX_TAGS_LEN 2
+static unsigned int scl_freq;
+module_param_named(scl_freq, scl_freq, uint, 0444);
+MODULE_PARM_DESC(scl_freq, "SCL frequency override");
+
/*
* count: no of blocks
* pos: current block number
{
struct qup_i2c_block *blk = &qup->blk;
struct i2c_msg *msg = qup->msg;
- u32 addr = msg->addr << 1;
+ u32 addr = i2c_8bit_addr_from_msg(msg);
u32 qup_tag;
int idx;
u32 val;
clk_disable_unprepare(qup->pclk);
}
+static const struct acpi_device_id qup_i2c_acpi_match[] = {
+ { "QCOM8010"},
+ { },
+};
+MODULE_DEVICE_TABLE(acpi, qup_i2c_acpi_match);
+
static int qup_i2c_probe(struct platform_device *pdev)
{
static const int blk_sizes[] = {4, 16, 32};
init_completion(&qup->xfer);
platform_set_drvdata(pdev, qup);
- ret = device_property_read_u32(qup->dev, "clock-frequency", &clk_freq);
- if (ret) {
- dev_notice(qup->dev, "using default clock-frequency %d",
- DEFAULT_CLK_FREQ);
+ if (scl_freq) {
+ dev_notice(qup->dev, "Using override frequency of %u\n", scl_freq);
+ clk_freq = scl_freq;
+ } else {
+ ret = device_property_read_u32(qup->dev, "clock-frequency", &clk_freq);
+ if (ret) {
+ dev_notice(qup->dev, "using default clock-frequency %d",
+ DEFAULT_CLK_FREQ);
+ }
}
if (of_device_is_compatible(pdev->dev.of_node, "qcom,i2c-qup-v1.1.1")) {
} else {
qup->adap.algo = &qup_i2c_algo_v2;
is_qup_v1 = false;
- ret = qup_i2c_req_dma(qup);
+ if (acpi_match_device(qup_i2c_acpi_match, qup->dev))
+ goto nodma;
+ else
+ ret = qup_i2c_req_dma(qup);
if (ret == -EPROBE_DEFER)
goto fail_dma;
}
nodma:
- /* We support frequencies up to FAST Mode (400KHz) */
- if (!clk_freq || clk_freq > 400000) {
+ /* We support frequencies up to FAST Mode Plus (1MHz) */
+ if (!clk_freq || clk_freq > I2C_FAST_MODE_PLUS_FREQ) {
dev_err(qup->dev, "clock frequency not supported %d\n",
clk_freq);
return -EINVAL;
size = QUP_INPUT_FIFO_SIZE(io_mode);
qup->in_fifo_sz = qup->in_blk_sz * (2 << size);
- fs_div = ((src_clk_freq / clk_freq) / 2) - 3;
hs_div = 3;
- qup->clk_ctl = (hs_div << 8) | (fs_div & 0xff);
+ if (clk_freq <= I2C_STANDARD_FREQ) {
+ fs_div = ((src_clk_freq / clk_freq) / 2) - 3;
+ qup->clk_ctl = (hs_div << 8) | (fs_div & 0xff);
+ } else {
+ /* 33%/66% duty cycle */
+ fs_div = ((src_clk_freq / clk_freq) - 6) * 2 / 3;
+ qup->clk_ctl = ((fs_div / 2) << 16) | (hs_div << 8) | (fs_div & 0xff);
+ }
/*
* Time it takes for a byte to be clocked out on the bus.
};
MODULE_DEVICE_TABLE(of, qup_i2c_dt_match);
-#if IS_ENABLED(CONFIG_ACPI)
-static const struct acpi_device_id qup_i2c_acpi_match[] = {
- { "QCOM8010"},
- { },
-};
-MODULE_DEVICE_TABLE(acpi, qup_i2c_acpi_match);
-#endif
-
static struct platform_driver qup_i2c_driver = {
.probe = qup_i2c_probe,
.remove = qup_i2c_remove,
if (priv->msgs_left == 1)
priv->flags |= ID_LAST_MSG;
- rcar_i2c_write(priv, ICMAR, (priv->msg->addr << 1) | read);
+ rcar_i2c_write(priv, ICMAR, i2c_8bit_addr_from_msg(priv->msg));
/*
* We don't have a test case but the HW engineers say that the write order
* of ICMSR and ICMCR depends on whether we issue START or REP_START. Since
* If next received data is the _LAST_, go to STOP phase. Might be
* overwritten by REP START when setting up a new msg. Not elegant
* but the only stable sequence for REP START I have found so far.
+ * If you want to change this code, make sure sending one transfer with
+ * four messages (WR-RD-WR-RD) works!
*/
if (priv->pos + 1 >= msg->len)
rcar_i2c_write(priv, ICMCR, RCAR_BUS_PHASE_STOP);
return IRQ_NONE;
if (riic->bytes_left == RIIC_INIT_MSG) {
- val = !!(riic->msg->flags & I2C_M_RD);
- if (val)
+ if (riic->msg->flags & I2C_M_RD)
/* On read, switch over to receive interrupt */
riic_clear_set_bit(riic, ICIER_TIE, ICIER_RIE, RIIC_ICIER);
else
/* On write, initialize length */
riic->bytes_left = riic->msg->len;
- val |= (riic->msg->addr << 1);
+ val = i2c_8bit_addr_from_msg(riic->msg);
} else {
val = *riic->buf;
riic->buf++;
if (ret < 0)
goto err_clk_notifier;
- dev_info(&pdev->dev, "Initialized RK3xxx I2C bus at %p\n", i2c->regs);
-
return 0;
err_clk_notifier:
{
struct osif_priv *priv = adapter->algo_data;
struct i2c_msg *pmsg;
- int ret = 0;
- int i, cmd;
+ int ret;
+ int i;
- for (i = 0; ret >= 0 && i < num; i++) {
+ for (i = 0; i < num; i++) {
pmsg = &msgs[i];
if (pmsg->flags & I2C_M_RD) {
- cmd = OSIFI2C_READ;
-
- ret = osif_usb_read(adapter, cmd, pmsg->flags,
- pmsg->addr, pmsg->buf,
- pmsg->len);
+ ret = osif_usb_read(adapter, OSIFI2C_READ,
+ pmsg->flags, pmsg->addr,
+ pmsg->buf, pmsg->len);
if (ret != pmsg->len) {
dev_err(&adapter->dev, "failure reading data\n");
return -EREMOTEIO;
}
} else {
- cmd = OSIFI2C_WRITE;
-
- ret = osif_usb_write(adapter, cmd, pmsg->flags,
- pmsg->addr, pmsg->buf, pmsg->len);
+ ret = osif_usb_write(adapter, OSIFI2C_WRITE,
+ pmsg->flags, pmsg->addr,
+ pmsg->buf, pmsg->len);
if (ret != pmsg->len) {
dev_err(&adapter->dev, "failure writing data\n");
return -EREMOTEIO;
{ .compatible = "samsung,s3c2440-i2c", .data = (void *)QUIRK_S3C2440 },
{ .compatible = "samsung,s3c2440-hdmiphy-i2c",
.data = (void *)(QUIRK_S3C2440 | QUIRK_HDMIPHY | QUIRK_NO_GPIO) },
- { .compatible = "samsung,exynos5440-i2c",
- .data = (void *)(QUIRK_S3C2440 | QUIRK_NO_GPIO) },
{ .compatible = "samsung,exynos5-sata-phy-i2c",
.data = (void *)(QUIRK_S3C2440 | QUIRK_POLL | QUIRK_NO_GPIO) },
{},
if (resource_size(res) > 0x17)
pd->flags |= IIC_FLAG_HAS_ICIC67;
- /* Enable Runtime PM for this device.
- *
- * Also tell the Runtime PM core to ignore children
- * for this device since it is valid for us to suspend
- * this I2C master driver even though the slave devices
- * on the I2C bus may not be suspended.
- *
- * The state of the I2C hardware bus is unaffected by
- * the Runtime PM state.
- */
- pm_suspend_ignore_children(&dev->dev, true);
pm_runtime_enable(&dev->dev);
pm_runtime_get_sync(&dev->dev);
--- /dev/null
+/*
+ * i2c-stm32.c
+ *
+ * Copyright (C) M'boumba Cedric Madianga 2017
+ * Author: M'boumba Cedric Madianga <cedric.madianga@gmail.com>
+ *
+ * License terms: GNU General Public License (GPL), version 2
+ */
+
+#include "i2c-stm32.h"
+
+/* Functions for DMA support */
+struct stm32_i2c_dma *stm32_i2c_dma_request(struct device *dev,
+ dma_addr_t phy_addr,
+ u32 txdr_offset,
+ u32 rxdr_offset)
+{
+ struct stm32_i2c_dma *dma;
+ struct dma_slave_config dma_sconfig;
+ int ret;
+
+ dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL);
+ if (!dma)
+ return NULL;
+
+ /* Request and configure I2C TX dma channel */
+ dma->chan_tx = dma_request_slave_channel(dev, "tx");
+ if (!dma->chan_tx) {
+ dev_dbg(dev, "can't request DMA tx channel\n");
+ ret = -EINVAL;
+ goto fail_al;
+ }
+
+ memset(&dma_sconfig, 0, sizeof(dma_sconfig));
+ dma_sconfig.dst_addr = phy_addr + txdr_offset;
+ dma_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+ dma_sconfig.dst_maxburst = 1;
+ dma_sconfig.direction = DMA_MEM_TO_DEV;
+ ret = dmaengine_slave_config(dma->chan_tx, &dma_sconfig);
+ if (ret < 0) {
+ dev_err(dev, "can't configure tx channel\n");
+ goto fail_tx;
+ }
+
+ /* Request and configure I2C RX dma channel */
+ dma->chan_rx = dma_request_slave_channel(dev, "rx");
+ if (!dma->chan_rx) {
+ dev_err(dev, "can't request DMA rx channel\n");
+ ret = -EINVAL;
+ goto fail_tx;
+ }
+
+ memset(&dma_sconfig, 0, sizeof(dma_sconfig));
+ dma_sconfig.src_addr = phy_addr + rxdr_offset;
+ dma_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+ dma_sconfig.src_maxburst = 1;
+ dma_sconfig.direction = DMA_DEV_TO_MEM;
+ ret = dmaengine_slave_config(dma->chan_rx, &dma_sconfig);
+ if (ret < 0) {
+ dev_err(dev, "can't configure rx channel\n");
+ goto fail_rx;
+ }
+
+ init_completion(&dma->dma_complete);
+
+ dev_info(dev, "using %s (tx) and %s (rx) for DMA transfers\n",
+ dma_chan_name(dma->chan_tx), dma_chan_name(dma->chan_rx));
+
+ return dma;
+
+fail_rx:
+ dma_release_channel(dma->chan_rx);
+fail_tx:
+ dma_release_channel(dma->chan_tx);
+fail_al:
+ devm_kfree(dev, dma);
+ dev_info(dev, "can't use DMA\n");
+
+ return NULL;
+}
+
+void stm32_i2c_dma_free(struct stm32_i2c_dma *dma)
+{
+ dma->dma_buf = 0;
+ dma->dma_len = 0;
+
+ dma_release_channel(dma->chan_tx);
+ dma->chan_tx = NULL;
+
+ dma_release_channel(dma->chan_rx);
+ dma->chan_rx = NULL;
+
+ dma->chan_using = NULL;
+}
+
+int stm32_i2c_prep_dma_xfer(struct device *dev, struct stm32_i2c_dma *dma,
+ bool rd_wr, u32 len, u8 *buf,
+ dma_async_tx_callback callback,
+ void *dma_async_param)
+{
+ struct dma_async_tx_descriptor *txdesc;
+ struct device *chan_dev;
+ int ret;
+
+ if (rd_wr) {
+ dma->chan_using = dma->chan_rx;
+ dma->dma_transfer_dir = DMA_DEV_TO_MEM;
+ dma->dma_data_dir = DMA_FROM_DEVICE;
+ } else {
+ dma->chan_using = dma->chan_tx;
+ dma->dma_transfer_dir = DMA_MEM_TO_DEV;
+ dma->dma_data_dir = DMA_TO_DEVICE;
+ }
+
+ dma->dma_len = len;
+ chan_dev = dma->chan_using->device->dev;
+
+ dma->dma_buf = dma_map_single(chan_dev, buf, dma->dma_len,
+ dma->dma_data_dir);
+ if (dma_mapping_error(chan_dev, dma->dma_buf)) {
+ dev_err(dev, "DMA mapping failed\n");
+ return -EINVAL;
+ }
+
+ txdesc = dmaengine_prep_slave_single(dma->chan_using, dma->dma_buf,
+ dma->dma_len,
+ dma->dma_transfer_dir,
+ DMA_PREP_INTERRUPT);
+ if (!txdesc) {
+ dev_err(dev, "Not able to get desc for DMA xfer\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ reinit_completion(&dma->dma_complete);
+
+ txdesc->callback = callback;
+ txdesc->callback_param = dma_async_param;
+ ret = dma_submit_error(dmaengine_submit(txdesc));
+ if (ret < 0) {
+ dev_err(dev, "DMA submit failed\n");
+ goto err;
+ }
+
+ dma_async_issue_pending(dma->chan_using);
+
+ return 0;
+
+err:
+ dma_unmap_single(chan_dev, dma->dma_buf, dma->dma_len,
+ dma->dma_data_dir);
+ return ret;
+}
#ifndef _I2C_STM32_H
#define _I2C_STM32_H
+#include <linux/dma-direction.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+
enum stm32_i2c_speed {
STM32_I2C_SPEED_STANDARD, /* 100 kHz */
STM32_I2C_SPEED_FAST, /* 400 kHz */
STM32_I2C_SPEED_END,
};
+/**
+ * struct stm32_i2c_dma - DMA specific data
+ * @chan_tx: dma channel for TX transfer
+ * @chan_rx: dma channel for RX transfer
+ * @chan_using: dma channel used for the current transfer (TX or RX)
+ * @dma_buf: dma buffer
+ * @dma_len: dma buffer len
+ * @dma_transfer_dir: dma transfer direction indicator
+ * @dma_data_dir: dma transfer mode indicator
+ * @dma_complete: dma transfer completion
+ */
+struct stm32_i2c_dma {
+ struct dma_chan *chan_tx;
+ struct dma_chan *chan_rx;
+ struct dma_chan *chan_using;
+ dma_addr_t dma_buf;
+ unsigned int dma_len;
+ enum dma_transfer_direction dma_transfer_dir;
+ enum dma_data_direction dma_data_dir;
+ struct completion dma_complete;
+};
+
+struct stm32_i2c_dma *stm32_i2c_dma_request(struct device *dev,
+ dma_addr_t phy_addr,
+ u32 txdr_offset, u32 rxdr_offset);
+
+void stm32_i2c_dma_free(struct stm32_i2c_dma *dma);
+
+int stm32_i2c_prep_dma_xfer(struct device *dev, struct stm32_i2c_dma *dma,
+ bool rd_wr, u32 len, u8 *buf,
+ dma_async_tx_callback callback,
+ void *dma_async_param);
+
#endif /* _I2C_STM32_H */
/* STM32F7 I2C registers */
#define STM32F7_I2C_CR1 0x00
#define STM32F7_I2C_CR2 0x04
+#define STM32F7_I2C_OAR1 0x08
+#define STM32F7_I2C_OAR2 0x0C
+#define STM32F7_I2C_PECR 0x20
#define STM32F7_I2C_TIMINGR 0x10
#define STM32F7_I2C_ISR 0x18
#define STM32F7_I2C_ICR 0x1C
#define STM32F7_I2C_TXDR 0x28
/* STM32F7 I2C control 1 */
+#define STM32F7_I2C_CR1_PECEN BIT(23)
+#define STM32F7_I2C_CR1_SBC BIT(16)
+#define STM32F7_I2C_CR1_RXDMAEN BIT(15)
+#define STM32F7_I2C_CR1_TXDMAEN BIT(14)
#define STM32F7_I2C_CR1_ANFOFF BIT(12)
#define STM32F7_I2C_CR1_ERRIE BIT(7)
#define STM32F7_I2C_CR1_TCIE BIT(6)
| STM32F7_I2C_CR1_NACKIE \
| STM32F7_I2C_CR1_RXIE \
| STM32F7_I2C_CR1_TXIE)
+#define STM32F7_I2C_XFER_IRQ_MASK (STM32F7_I2C_CR1_TCIE \
+ | STM32F7_I2C_CR1_STOPIE \
+ | STM32F7_I2C_CR1_NACKIE \
+ | STM32F7_I2C_CR1_RXIE \
+ | STM32F7_I2C_CR1_TXIE)
/* STM32F7 I2C control 2 */
+#define STM32F7_I2C_CR2_PECBYTE BIT(26)
#define STM32F7_I2C_CR2_RELOAD BIT(24)
#define STM32F7_I2C_CR2_NBYTES_MASK GENMASK(23, 16)
#define STM32F7_I2C_CR2_NBYTES(n) (((n) & 0xff) << 16)
#define STM32F7_I2C_CR2_NACK BIT(15)
#define STM32F7_I2C_CR2_STOP BIT(14)
#define STM32F7_I2C_CR2_START BIT(13)
+#define STM32F7_I2C_CR2_HEAD10R BIT(12)
+#define STM32F7_I2C_CR2_ADD10 BIT(11)
#define STM32F7_I2C_CR2_RD_WRN BIT(10)
+#define STM32F7_I2C_CR2_SADD10_MASK GENMASK(9, 0)
+#define STM32F7_I2C_CR2_SADD10(n) (((n) & \
+ STM32F7_I2C_CR2_SADD10_MASK))
#define STM32F7_I2C_CR2_SADD7_MASK GENMASK(7, 1)
#define STM32F7_I2C_CR2_SADD7(n) (((n) & 0x7f) << 1)
+/* STM32F7 I2C Own Address 1 */
+#define STM32F7_I2C_OAR1_OA1EN BIT(15)
+#define STM32F7_I2C_OAR1_OA1MODE BIT(10)
+#define STM32F7_I2C_OAR1_OA1_10_MASK GENMASK(9, 0)
+#define STM32F7_I2C_OAR1_OA1_10(n) (((n) & \
+ STM32F7_I2C_OAR1_OA1_10_MASK))
+#define STM32F7_I2C_OAR1_OA1_7_MASK GENMASK(7, 1)
+#define STM32F7_I2C_OAR1_OA1_7(n) (((n) & 0x7f) << 1)
+#define STM32F7_I2C_OAR1_MASK (STM32F7_I2C_OAR1_OA1_7_MASK \
+ | STM32F7_I2C_OAR1_OA1_10_MASK \
+ | STM32F7_I2C_OAR1_OA1EN \
+ | STM32F7_I2C_OAR1_OA1MODE)
+
+/* STM32F7 I2C Own Address 2 */
+#define STM32F7_I2C_OAR2_OA2EN BIT(15)
+#define STM32F7_I2C_OAR2_OA2MSK_MASK GENMASK(10, 8)
+#define STM32F7_I2C_OAR2_OA2MSK(n) (((n) & 0x7) << 8)
+#define STM32F7_I2C_OAR2_OA2_7_MASK GENMASK(7, 1)
+#define STM32F7_I2C_OAR2_OA2_7(n) (((n) & 0x7f) << 1)
+#define STM32F7_I2C_OAR2_MASK (STM32F7_I2C_OAR2_OA2MSK_MASK \
+ | STM32F7_I2C_OAR2_OA2_7_MASK \
+ | STM32F7_I2C_OAR2_OA2EN)
+
/* STM32F7 I2C Interrupt Status */
+#define STM32F7_I2C_ISR_ADDCODE_MASK GENMASK(23, 17)
+#define STM32F7_I2C_ISR_ADDCODE_GET(n) \
+ (((n) & STM32F7_I2C_ISR_ADDCODE_MASK) >> 17)
+#define STM32F7_I2C_ISR_DIR BIT(16)
#define STM32F7_I2C_ISR_BUSY BIT(15)
+#define STM32F7_I2C_ISR_PECERR BIT(11)
#define STM32F7_I2C_ISR_ARLO BIT(9)
#define STM32F7_I2C_ISR_BERR BIT(8)
#define STM32F7_I2C_ISR_TCR BIT(7)
#define STM32F7_I2C_ISR_TC BIT(6)
#define STM32F7_I2C_ISR_STOPF BIT(5)
#define STM32F7_I2C_ISR_NACKF BIT(4)
+#define STM32F7_I2C_ISR_ADDR BIT(3)
#define STM32F7_I2C_ISR_RXNE BIT(2)
#define STM32F7_I2C_ISR_TXIS BIT(1)
+#define STM32F7_I2C_ISR_TXE BIT(0)
/* STM32F7 I2C Interrupt Clear */
+#define STM32F7_I2C_ICR_PECCF BIT(11)
#define STM32F7_I2C_ICR_ARLOCF BIT(9)
#define STM32F7_I2C_ICR_BERRCF BIT(8)
#define STM32F7_I2C_ICR_STOPCF BIT(5)
#define STM32F7_I2C_ICR_NACKCF BIT(4)
+#define STM32F7_I2C_ICR_ADDRCF BIT(3)
/* STM32F7 I2C Timing */
#define STM32F7_I2C_TIMINGR_PRESC(n) (((n) & 0xf) << 28)
#define STM32F7_I2C_TIMINGR_SCLL(n) ((n) & 0xff)
#define STM32F7_I2C_MAX_LEN 0xff
+#define STM32F7_I2C_DMA_LEN_MIN 0x16
+#define STM32F7_I2C_MAX_SLAVE 0x2
#define STM32F7_I2C_DNF_DEFAULT 0
#define STM32F7_I2C_DNF_MAX 16
/**
* struct stm32f7_i2c_timings - private I2C output parameters
- * @prec: Prescaler value
+ * @node: List entry
+ * @presc: Prescaler value
* @scldel: Data setup time
* @sdadel: Data hold time
* @sclh: SCL high period (master mode)
- * @sclh: SCL low period (master mode)
+ * @scll: SCL low period (master mode)
*/
struct stm32f7_i2c_timings {
struct list_head node;
/**
* struct stm32f7_i2c_msg - client specific data
- * @addr: 8-bit slave addr, including r/w bit
+ * @addr: 8-bit or 10-bit slave addr, including r/w bit
* @count: number of bytes to be transferred
* @buf: data buffer
* @result: result of the transfer
* @stop: last I2C msg to be sent, i.e. STOP to be generated
+ * @smbus: boolean to know if the I2C IP is used in SMBus mode
+ * @size: type of SMBus protocol
+ * @read_write: direction of SMBus protocol
+ * SMBus block read and SMBus block write - block read process call protocols
+ * @smbus_buf: buffer to be used for SMBus protocol transfer. It will
+ * contain a maximum of 32 bytes of data + byte command + byte count + PEC
+ * This buffer has to be 32-bit aligned to be compliant with memory address
+ * register in DMA mode.
*/
struct stm32f7_i2c_msg {
- u8 addr;
+ u16 addr;
u32 count;
u8 *buf;
int result;
bool stop;
+ bool smbus;
+ int size;
+ char read_write;
+ u8 smbus_buf[I2C_SMBUS_BLOCK_MAX + 3] __aligned(4);
};
/**
* @f7_msg: customized i2c msg for driver usage
* @setup: I2C timing input setup
* @timing: I2C computed timings
+ * @slave: list of slave devices registered on the I2C bus
+ * @slave_running: slave device currently used
+ * @slave_dir: transfer direction for the current slave device
+ * @master_mode: boolean to know in which mode the I2C is running (master or
+ * slave)
+ * @dma: dma data
+ * @use_dma: boolean to know if dma is used in the current transfer
*/
struct stm32f7_i2c_dev {
struct i2c_adapter adap;
struct stm32f7_i2c_msg f7_msg;
struct stm32f7_i2c_setup setup;
struct stm32f7_i2c_timings timing;
+ struct i2c_client *slave[STM32F7_I2C_MAX_SLAVE];
+ struct i2c_client *slave_running;
+ u32 slave_dir;
+ bool master_mode;
+ struct stm32_i2c_dma *dma;
+ bool use_dma;
};
/**
writel_relaxed(readl_relaxed(reg) & ~mask, reg);
}
+static void stm32f7_i2c_disable_irq(struct stm32f7_i2c_dev *i2c_dev, u32 mask)
+{
+ stm32f7_i2c_clr_bits(i2c_dev->base + STM32F7_I2C_CR1, mask);
+}
+
static int stm32f7_i2c_compute_timing(struct stm32f7_i2c_dev *i2c_dev,
struct stm32f7_i2c_setup *setup,
struct stm32f7_i2c_timings *output)
return 0;
}
+static void stm32f7_i2c_disable_dma_req(struct stm32f7_i2c_dev *i2c_dev)
+{
+ void __iomem *base = i2c_dev->base;
+ u32 mask = STM32F7_I2C_CR1_RXDMAEN | STM32F7_I2C_CR1_TXDMAEN;
+
+ stm32f7_i2c_clr_bits(base + STM32F7_I2C_CR1, mask);
+}
+
+static void stm32f7_i2c_dma_callback(void *arg)
+{
+ struct stm32f7_i2c_dev *i2c_dev = (struct stm32f7_i2c_dev *)arg;
+ struct stm32_i2c_dma *dma = i2c_dev->dma;
+ struct device *dev = dma->chan_using->device->dev;
+
+ stm32f7_i2c_disable_dma_req(i2c_dev);
+ dma_unmap_single(dev, dma->dma_buf, dma->dma_len, dma->dma_data_dir);
+ complete(&dma->dma_complete);
+}
+
static void stm32f7_i2c_hw_config(struct stm32f7_i2c_dev *i2c_dev)
{
struct stm32f7_i2c_timings *t = &i2c_dev->timing;
if (f7_msg->count) {
*f7_msg->buf++ = readb_relaxed(base + STM32F7_I2C_RXDR);
f7_msg->count--;
+ } else {
+ /* Flush RX buffer has no data is expected */
+ readb_relaxed(base + STM32F7_I2C_RXDR);
}
}
struct stm32f7_i2c_msg *f7_msg = &i2c_dev->f7_msg;
u32 cr2;
+ if (i2c_dev->use_dma)
+ f7_msg->count -= STM32F7_I2C_MAX_LEN;
+
cr2 = readl_relaxed(i2c_dev->base + STM32F7_I2C_CR2);
cr2 &= ~STM32F7_I2C_CR2_NBYTES_MASK;
writel_relaxed(cr2, i2c_dev->base + STM32F7_I2C_CR2);
}
+static void stm32f7_i2c_smbus_reload(struct stm32f7_i2c_dev *i2c_dev)
+{
+ struct stm32f7_i2c_msg *f7_msg = &i2c_dev->f7_msg;
+ u32 cr2;
+ u8 *val;
+
+ /*
+ * For I2C_SMBUS_BLOCK_DATA && I2C_SMBUS_BLOCK_PROC_CALL, the first
+ * data received inform us how many data will follow.
+ */
+ stm32f7_i2c_read_rx_data(i2c_dev);
+
+ /*
+ * Update NBYTES with the value read to continue the transfer
+ */
+ val = f7_msg->buf - sizeof(u8);
+ f7_msg->count = *val;
+ cr2 = readl_relaxed(i2c_dev->base + STM32F7_I2C_CR2);
+ cr2 &= ~(STM32F7_I2C_CR2_NBYTES_MASK | STM32F7_I2C_CR2_RELOAD);
+ cr2 |= STM32F7_I2C_CR2_NBYTES(f7_msg->count);
+ writel_relaxed(cr2, i2c_dev->base + STM32F7_I2C_CR2);
+}
+
+static int stm32f7_i2c_release_bus(struct i2c_adapter *i2c_adap)
+{
+ struct stm32f7_i2c_dev *i2c_dev = i2c_get_adapdata(i2c_adap);
+
+ dev_info(i2c_dev->dev, "Trying to recover bus\n");
+
+ stm32f7_i2c_clr_bits(i2c_dev->base + STM32F7_I2C_CR1,
+ STM32F7_I2C_CR1_PE);
+
+ stm32f7_i2c_hw_config(i2c_dev);
+
+ return 0;
+}
+
static int stm32f7_i2c_wait_free_bus(struct stm32f7_i2c_dev *i2c_dev)
{
u32 status;
status,
!(status & STM32F7_I2C_ISR_BUSY),
10, 1000);
+ if (!ret)
+ return 0;
+
+ dev_info(i2c_dev->dev, "bus busy\n");
+
+ ret = stm32f7_i2c_release_bus(&i2c_dev->adap);
if (ret) {
- dev_dbg(i2c_dev->dev, "bus busy\n");
- ret = -EBUSY;
+ dev_err(i2c_dev->dev, "Failed to recover the bus (%d)\n", ret);
+ return ret;
}
- return ret;
+ return -EBUSY;
}
static void stm32f7_i2c_xfer_msg(struct stm32f7_i2c_dev *i2c_dev,
struct stm32f7_i2c_msg *f7_msg = &i2c_dev->f7_msg;
void __iomem *base = i2c_dev->base;
u32 cr1, cr2;
+ int ret;
f7_msg->addr = msg->addr;
f7_msg->buf = msg->buf;
cr2 |= STM32F7_I2C_CR2_RD_WRN;
/* Set slave address */
- cr2 &= ~STM32F7_I2C_CR2_SADD7_MASK;
- cr2 |= STM32F7_I2C_CR2_SADD7(f7_msg->addr);
+ cr2 &= ~(STM32F7_I2C_CR2_HEAD10R | STM32F7_I2C_CR2_ADD10);
+ if (msg->flags & I2C_M_TEN) {
+ cr2 &= ~STM32F7_I2C_CR2_SADD10_MASK;
+ cr2 |= STM32F7_I2C_CR2_SADD10(f7_msg->addr);
+ cr2 |= STM32F7_I2C_CR2_ADD10;
+ } else {
+ cr2 &= ~STM32F7_I2C_CR2_SADD7_MASK;
+ cr2 |= STM32F7_I2C_CR2_SADD7(f7_msg->addr);
+ }
/* Set nb bytes to transfer and reload if needed */
cr2 &= ~(STM32F7_I2C_CR2_NBYTES_MASK | STM32F7_I2C_CR2_RELOAD);
cr1 |= STM32F7_I2C_CR1_ERRIE | STM32F7_I2C_CR1_TCIE |
STM32F7_I2C_CR1_STOPIE | STM32F7_I2C_CR1_NACKIE;
- /* Clear TX/RX interrupt */
+ /* Clear DMA req and TX/RX interrupt */
+ cr1 &= ~(STM32F7_I2C_CR1_RXIE | STM32F7_I2C_CR1_TXIE |
+ STM32F7_I2C_CR1_RXDMAEN | STM32F7_I2C_CR1_TXDMAEN);
+
+ /* Configure DMA or enable RX/TX interrupt */
+ i2c_dev->use_dma = false;
+ if (i2c_dev->dma && f7_msg->count >= STM32F7_I2C_DMA_LEN_MIN) {
+ ret = stm32_i2c_prep_dma_xfer(i2c_dev->dev, i2c_dev->dma,
+ msg->flags & I2C_M_RD,
+ f7_msg->count, f7_msg->buf,
+ stm32f7_i2c_dma_callback,
+ i2c_dev);
+ if (!ret)
+ i2c_dev->use_dma = true;
+ else
+ dev_warn(i2c_dev->dev, "can't use DMA\n");
+ }
+
+ if (!i2c_dev->use_dma) {
+ if (msg->flags & I2C_M_RD)
+ cr1 |= STM32F7_I2C_CR1_RXIE;
+ else
+ cr1 |= STM32F7_I2C_CR1_TXIE;
+ } else {
+ if (msg->flags & I2C_M_RD)
+ cr1 |= STM32F7_I2C_CR1_RXDMAEN;
+ else
+ cr1 |= STM32F7_I2C_CR1_TXDMAEN;
+ }
+
+ /* Configure Start/Repeated Start */
+ cr2 |= STM32F7_I2C_CR2_START;
+
+ i2c_dev->master_mode = true;
+
+ /* Write configurations registers */
+ writel_relaxed(cr1, base + STM32F7_I2C_CR1);
+ writel_relaxed(cr2, base + STM32F7_I2C_CR2);
+}
+
+static int stm32f7_i2c_smbus_xfer_msg(struct stm32f7_i2c_dev *i2c_dev,
+ unsigned short flags, u8 command,
+ union i2c_smbus_data *data)
+{
+ struct stm32f7_i2c_msg *f7_msg = &i2c_dev->f7_msg;
+ struct device *dev = i2c_dev->dev;
+ void __iomem *base = i2c_dev->base;
+ u32 cr1, cr2;
+ int i, ret;
+
+ f7_msg->result = 0;
+ reinit_completion(&i2c_dev->complete);
+
+ cr2 = readl_relaxed(base + STM32F7_I2C_CR2);
+ cr1 = readl_relaxed(base + STM32F7_I2C_CR1);
+
+ /* Set transfer direction */
+ cr2 &= ~STM32F7_I2C_CR2_RD_WRN;
+ if (f7_msg->read_write)
+ cr2 |= STM32F7_I2C_CR2_RD_WRN;
+
+ /* Set slave address */
+ cr2 &= ~(STM32F7_I2C_CR2_ADD10 | STM32F7_I2C_CR2_SADD7_MASK);
+ cr2 |= STM32F7_I2C_CR2_SADD7(f7_msg->addr);
+
+ f7_msg->smbus_buf[0] = command;
+ switch (f7_msg->size) {
+ case I2C_SMBUS_QUICK:
+ f7_msg->stop = true;
+ f7_msg->count = 0;
+ break;
+ case I2C_SMBUS_BYTE:
+ f7_msg->stop = true;
+ f7_msg->count = 1;
+ break;
+ case I2C_SMBUS_BYTE_DATA:
+ if (f7_msg->read_write) {
+ f7_msg->stop = false;
+ f7_msg->count = 1;
+ cr2 &= ~STM32F7_I2C_CR2_RD_WRN;
+ } else {
+ f7_msg->stop = true;
+ f7_msg->count = 2;
+ f7_msg->smbus_buf[1] = data->byte;
+ }
+ break;
+ case I2C_SMBUS_WORD_DATA:
+ if (f7_msg->read_write) {
+ f7_msg->stop = false;
+ f7_msg->count = 1;
+ cr2 &= ~STM32F7_I2C_CR2_RD_WRN;
+ } else {
+ f7_msg->stop = true;
+ f7_msg->count = 3;
+ f7_msg->smbus_buf[1] = data->word & 0xff;
+ f7_msg->smbus_buf[2] = data->word >> 8;
+ }
+ break;
+ case I2C_SMBUS_BLOCK_DATA:
+ if (f7_msg->read_write) {
+ f7_msg->stop = false;
+ f7_msg->count = 1;
+ cr2 &= ~STM32F7_I2C_CR2_RD_WRN;
+ } else {
+ f7_msg->stop = true;
+ if (data->block[0] > I2C_SMBUS_BLOCK_MAX ||
+ !data->block[0]) {
+ dev_err(dev, "Invalid block write size %d\n",
+ data->block[0]);
+ return -EINVAL;
+ }
+ f7_msg->count = data->block[0] + 2;
+ for (i = 1; i < f7_msg->count; i++)
+ f7_msg->smbus_buf[i] = data->block[i - 1];
+ }
+ break;
+ case I2C_SMBUS_PROC_CALL:
+ f7_msg->stop = false;
+ f7_msg->count = 3;
+ f7_msg->smbus_buf[1] = data->word & 0xff;
+ f7_msg->smbus_buf[2] = data->word >> 8;
+ cr2 &= ~STM32F7_I2C_CR2_RD_WRN;
+ f7_msg->read_write = I2C_SMBUS_READ;
+ break;
+ case I2C_SMBUS_BLOCK_PROC_CALL:
+ f7_msg->stop = false;
+ if (data->block[0] > I2C_SMBUS_BLOCK_MAX - 1) {
+ dev_err(dev, "Invalid block write size %d\n",
+ data->block[0]);
+ return -EINVAL;
+ }
+ f7_msg->count = data->block[0] + 2;
+ for (i = 1; i < f7_msg->count; i++)
+ f7_msg->smbus_buf[i] = data->block[i - 1];
+ cr2 &= ~STM32F7_I2C_CR2_RD_WRN;
+ f7_msg->read_write = I2C_SMBUS_READ;
+ break;
+ default:
+ dev_err(dev, "Unsupported smbus protocol %d\n", f7_msg->size);
+ return -EOPNOTSUPP;
+ }
+
+ f7_msg->buf = f7_msg->smbus_buf;
+
+ /* Configure PEC */
+ if ((flags & I2C_CLIENT_PEC) && f7_msg->size != I2C_SMBUS_QUICK) {
+ cr1 |= STM32F7_I2C_CR1_PECEN;
+ cr2 |= STM32F7_I2C_CR2_PECBYTE;
+ if (!f7_msg->read_write)
+ f7_msg->count++;
+ } else {
+ cr1 &= ~STM32F7_I2C_CR1_PECEN;
+ cr2 &= ~STM32F7_I2C_CR2_PECBYTE;
+ }
+
+ /* Set number of bytes to be transferred */
+ cr2 &= ~(STM32F7_I2C_CR2_NBYTES_MASK | STM32F7_I2C_CR2_RELOAD);
+ cr2 |= STM32F7_I2C_CR2_NBYTES(f7_msg->count);
+
+ /* Enable NACK, STOP, error and transfer complete interrupts */
+ cr1 |= STM32F7_I2C_CR1_ERRIE | STM32F7_I2C_CR1_TCIE |
+ STM32F7_I2C_CR1_STOPIE | STM32F7_I2C_CR1_NACKIE;
+
+ /* Clear DMA req and TX/RX interrupt */
+ cr1 &= ~(STM32F7_I2C_CR1_RXIE | STM32F7_I2C_CR1_TXIE |
+ STM32F7_I2C_CR1_RXDMAEN | STM32F7_I2C_CR1_TXDMAEN);
+
+ /* Configure DMA or enable RX/TX interrupt */
+ i2c_dev->use_dma = false;
+ if (i2c_dev->dma && f7_msg->count >= STM32F7_I2C_DMA_LEN_MIN) {
+ ret = stm32_i2c_prep_dma_xfer(i2c_dev->dev, i2c_dev->dma,
+ cr2 & STM32F7_I2C_CR2_RD_WRN,
+ f7_msg->count, f7_msg->buf,
+ stm32f7_i2c_dma_callback,
+ i2c_dev);
+ if (!ret)
+ i2c_dev->use_dma = true;
+ else
+ dev_warn(i2c_dev->dev, "can't use DMA\n");
+ }
+
+ if (!i2c_dev->use_dma) {
+ if (cr2 & STM32F7_I2C_CR2_RD_WRN)
+ cr1 |= STM32F7_I2C_CR1_RXIE;
+ else
+ cr1 |= STM32F7_I2C_CR1_TXIE;
+ } else {
+ if (cr2 & STM32F7_I2C_CR2_RD_WRN)
+ cr1 |= STM32F7_I2C_CR1_RXDMAEN;
+ else
+ cr1 |= STM32F7_I2C_CR1_TXDMAEN;
+ }
+
+ /* Set Start bit */
+ cr2 |= STM32F7_I2C_CR2_START;
+
+ i2c_dev->master_mode = true;
+
+ /* Write configurations registers */
+ writel_relaxed(cr1, base + STM32F7_I2C_CR1);
+ writel_relaxed(cr2, base + STM32F7_I2C_CR2);
+
+ return 0;
+}
+
+static void stm32f7_i2c_smbus_rep_start(struct stm32f7_i2c_dev *i2c_dev)
+{
+ struct stm32f7_i2c_msg *f7_msg = &i2c_dev->f7_msg;
+ void __iomem *base = i2c_dev->base;
+ u32 cr1, cr2;
+ int ret;
+
+ cr2 = readl_relaxed(base + STM32F7_I2C_CR2);
+ cr1 = readl_relaxed(base + STM32F7_I2C_CR1);
+
+ /* Set transfer direction */
+ cr2 |= STM32F7_I2C_CR2_RD_WRN;
+
+ switch (f7_msg->size) {
+ case I2C_SMBUS_BYTE_DATA:
+ f7_msg->count = 1;
+ break;
+ case I2C_SMBUS_WORD_DATA:
+ case I2C_SMBUS_PROC_CALL:
+ f7_msg->count = 2;
+ break;
+ case I2C_SMBUS_BLOCK_DATA:
+ case I2C_SMBUS_BLOCK_PROC_CALL:
+ f7_msg->count = 1;
+ cr2 |= STM32F7_I2C_CR2_RELOAD;
+ break;
+ }
+
+ f7_msg->buf = f7_msg->smbus_buf;
+ f7_msg->stop = true;
+
+ /* Add one byte for PEC if needed */
+ if (cr1 & STM32F7_I2C_CR1_PECEN)
+ f7_msg->count++;
+
+ /* Set number of bytes to be transferred */
+ cr2 &= ~(STM32F7_I2C_CR2_NBYTES_MASK);
+ cr2 |= STM32F7_I2C_CR2_NBYTES(f7_msg->count);
+
+ /*
+ * Configure RX/TX interrupt:
+ */
cr1 &= ~(STM32F7_I2C_CR1_RXIE | STM32F7_I2C_CR1_TXIE);
+ cr1 |= STM32F7_I2C_CR1_RXIE;
- /* Enable RX/TX interrupt according to msg direction */
- if (msg->flags & I2C_M_RD)
+ /*
+ * Configure DMA or enable RX/TX interrupt:
+ * For I2C_SMBUS_BLOCK_DATA and I2C_SMBUS_BLOCK_PROC_CALL we don't use
+ * dma as we don't know in advance how many data will be received
+ */
+ cr1 &= ~(STM32F7_I2C_CR1_RXIE | STM32F7_I2C_CR1_TXIE |
+ STM32F7_I2C_CR1_RXDMAEN | STM32F7_I2C_CR1_TXDMAEN);
+
+ i2c_dev->use_dma = false;
+ if (i2c_dev->dma && f7_msg->count >= STM32F7_I2C_DMA_LEN_MIN &&
+ f7_msg->size != I2C_SMBUS_BLOCK_DATA &&
+ f7_msg->size != I2C_SMBUS_BLOCK_PROC_CALL) {
+ ret = stm32_i2c_prep_dma_xfer(i2c_dev->dev, i2c_dev->dma,
+ cr2 & STM32F7_I2C_CR2_RD_WRN,
+ f7_msg->count, f7_msg->buf,
+ stm32f7_i2c_dma_callback,
+ i2c_dev);
+
+ if (!ret)
+ i2c_dev->use_dma = true;
+ else
+ dev_warn(i2c_dev->dev, "can't use DMA\n");
+ }
+
+ if (!i2c_dev->use_dma)
cr1 |= STM32F7_I2C_CR1_RXIE;
else
- cr1 |= STM32F7_I2C_CR1_TXIE;
+ cr1 |= STM32F7_I2C_CR1_RXDMAEN;
- /* Configure Start/Repeated Start */
+ /* Configure Repeated Start */
cr2 |= STM32F7_I2C_CR2_START;
/* Write configurations registers */
writel_relaxed(cr2, base + STM32F7_I2C_CR2);
}
-static void stm32f7_i2c_disable_irq(struct stm32f7_i2c_dev *i2c_dev, u32 mask)
+static int stm32f7_i2c_smbus_check_pec(struct stm32f7_i2c_dev *i2c_dev)
{
- stm32f7_i2c_clr_bits(i2c_dev->base + STM32F7_I2C_CR1, mask);
+ struct stm32f7_i2c_msg *f7_msg = &i2c_dev->f7_msg;
+ u8 count, internal_pec, received_pec;
+
+ internal_pec = readl_relaxed(i2c_dev->base + STM32F7_I2C_PECR);
+
+ switch (f7_msg->size) {
+ case I2C_SMBUS_BYTE:
+ case I2C_SMBUS_BYTE_DATA:
+ received_pec = f7_msg->smbus_buf[1];
+ break;
+ case I2C_SMBUS_WORD_DATA:
+ case I2C_SMBUS_PROC_CALL:
+ received_pec = f7_msg->smbus_buf[2];
+ break;
+ case I2C_SMBUS_BLOCK_DATA:
+ case I2C_SMBUS_BLOCK_PROC_CALL:
+ count = f7_msg->smbus_buf[0];
+ received_pec = f7_msg->smbus_buf[count];
+ break;
+ default:
+ dev_err(i2c_dev->dev, "Unsupported smbus protocol for PEC\n");
+ return -EINVAL;
+ }
+
+ if (internal_pec != received_pec) {
+ dev_err(i2c_dev->dev, "Bad PEC 0x%02x vs. 0x%02x\n",
+ internal_pec, received_pec);
+ return -EBADMSG;
+ }
+
+ return 0;
+}
+
+static bool stm32f7_i2c_is_addr_match(struct i2c_client *slave, u32 addcode)
+{
+ u32 addr;
+
+ if (!slave)
+ return false;
+
+ if (slave->flags & I2C_CLIENT_TEN) {
+ /*
+ * For 10-bit addr, addcode = 11110XY with
+ * X = Bit 9 of slave address
+ * Y = Bit 8 of slave address
+ */
+ addr = slave->addr >> 8;
+ addr |= 0x78;
+ if (addr == addcode)
+ return true;
+ } else {
+ addr = slave->addr & 0x7f;
+ if (addr == addcode)
+ return true;
+ }
+
+ return false;
+}
+
+static void stm32f7_i2c_slave_start(struct stm32f7_i2c_dev *i2c_dev)
+{
+ struct i2c_client *slave = i2c_dev->slave_running;
+ void __iomem *base = i2c_dev->base;
+ u32 mask;
+ u8 value = 0;
+
+ if (i2c_dev->slave_dir) {
+ /* Notify i2c slave that new read transfer is starting */
+ i2c_slave_event(slave, I2C_SLAVE_READ_REQUESTED, &value);
+
+ /*
+ * Disable slave TX config in case of I2C combined message
+ * (I2C Write followed by I2C Read)
+ */
+ mask = STM32F7_I2C_CR2_RELOAD;
+ stm32f7_i2c_clr_bits(base + STM32F7_I2C_CR2, mask);
+ mask = STM32F7_I2C_CR1_SBC | STM32F7_I2C_CR1_RXIE |
+ STM32F7_I2C_CR1_TCIE;
+ stm32f7_i2c_clr_bits(base + STM32F7_I2C_CR1, mask);
+
+ /* Enable TX empty, STOP, NACK interrupts */
+ mask = STM32F7_I2C_CR1_STOPIE | STM32F7_I2C_CR1_NACKIE |
+ STM32F7_I2C_CR1_TXIE;
+ stm32f7_i2c_set_bits(base + STM32F7_I2C_CR1, mask);
+
+ } else {
+ /* Notify i2c slave that new write transfer is starting */
+ i2c_slave_event(slave, I2C_SLAVE_WRITE_REQUESTED, &value);
+
+ /* Set reload mode to be able to ACK/NACK each received byte */
+ mask = STM32F7_I2C_CR2_RELOAD;
+ stm32f7_i2c_set_bits(base + STM32F7_I2C_CR2, mask);
+
+ /*
+ * Set STOP, NACK, RX empty and transfer complete interrupts.*
+ * Set Slave Byte Control to be able to ACK/NACK each data
+ * byte received
+ */
+ mask = STM32F7_I2C_CR1_STOPIE | STM32F7_I2C_CR1_NACKIE |
+ STM32F7_I2C_CR1_SBC | STM32F7_I2C_CR1_RXIE |
+ STM32F7_I2C_CR1_TCIE;
+ stm32f7_i2c_set_bits(base + STM32F7_I2C_CR1, mask);
+ }
+}
+
+static void stm32f7_i2c_slave_addr(struct stm32f7_i2c_dev *i2c_dev)
+{
+ void __iomem *base = i2c_dev->base;
+ u32 isr, addcode, dir, mask;
+ int i;
+
+ isr = readl_relaxed(i2c_dev->base + STM32F7_I2C_ISR);
+ addcode = STM32F7_I2C_ISR_ADDCODE_GET(isr);
+ dir = isr & STM32F7_I2C_ISR_DIR;
+
+ for (i = 0; i < STM32F7_I2C_MAX_SLAVE; i++) {
+ if (stm32f7_i2c_is_addr_match(i2c_dev->slave[i], addcode)) {
+ i2c_dev->slave_running = i2c_dev->slave[i];
+ i2c_dev->slave_dir = dir;
+
+ /* Start I2C slave processing */
+ stm32f7_i2c_slave_start(i2c_dev);
+
+ /* Clear ADDR flag */
+ mask = STM32F7_I2C_ICR_ADDRCF;
+ writel_relaxed(mask, base + STM32F7_I2C_ICR);
+ break;
+ }
+ }
+}
+
+static int stm32f7_i2c_get_slave_id(struct stm32f7_i2c_dev *i2c_dev,
+ struct i2c_client *slave, int *id)
+{
+ int i;
+
+ for (i = 0; i < STM32F7_I2C_MAX_SLAVE; i++) {
+ if (i2c_dev->slave[i] == slave) {
+ *id = i;
+ return 0;
+ }
+ }
+
+ dev_err(i2c_dev->dev, "Slave 0x%x not registered\n", slave->addr);
+
+ return -ENODEV;
+}
+
+static int stm32f7_i2c_get_free_slave_id(struct stm32f7_i2c_dev *i2c_dev,
+ struct i2c_client *slave, int *id)
+{
+ struct device *dev = i2c_dev->dev;
+ int i;
+
+ /*
+ * slave[0] supports 7-bit and 10-bit slave address
+ * slave[1] supports 7-bit slave address only
+ */
+ for (i = 0; i < STM32F7_I2C_MAX_SLAVE; i++) {
+ if (i == 1 && (slave->flags & I2C_CLIENT_PEC))
+ continue;
+ if (!i2c_dev->slave[i]) {
+ *id = i;
+ return 0;
+ }
+ }
+
+ dev_err(dev, "Slave 0x%x could not be registered\n", slave->addr);
+
+ return -EINVAL;
+}
+
+static bool stm32f7_i2c_is_slave_registered(struct stm32f7_i2c_dev *i2c_dev)
+{
+ int i;
+
+ for (i = 0; i < STM32F7_I2C_MAX_SLAVE; i++) {
+ if (i2c_dev->slave[i])
+ return true;
+ }
+
+ return false;
+}
+
+static bool stm32f7_i2c_is_slave_busy(struct stm32f7_i2c_dev *i2c_dev)
+{
+ int i, busy;
+
+ busy = 0;
+ for (i = 0; i < STM32F7_I2C_MAX_SLAVE; i++) {
+ if (i2c_dev->slave[i])
+ busy++;
+ }
+
+ return i == busy;
+}
+
+static irqreturn_t stm32f7_i2c_slave_isr_event(struct stm32f7_i2c_dev *i2c_dev)
+{
+ void __iomem *base = i2c_dev->base;
+ u32 cr2, status, mask;
+ u8 val;
+ int ret;
+
+ status = readl_relaxed(i2c_dev->base + STM32F7_I2C_ISR);
+
+ /* Slave transmitter mode */
+ if (status & STM32F7_I2C_ISR_TXIS) {
+ i2c_slave_event(i2c_dev->slave_running,
+ I2C_SLAVE_READ_PROCESSED,
+ &val);
+
+ /* Write data byte */
+ writel_relaxed(val, base + STM32F7_I2C_TXDR);
+ }
+
+ /* Transfer Complete Reload for Slave receiver mode */
+ if (status & STM32F7_I2C_ISR_TCR || status & STM32F7_I2C_ISR_RXNE) {
+ /*
+ * Read data byte then set NBYTES to receive next byte or NACK
+ * the current received byte
+ */
+ val = readb_relaxed(i2c_dev->base + STM32F7_I2C_RXDR);
+ ret = i2c_slave_event(i2c_dev->slave_running,
+ I2C_SLAVE_WRITE_RECEIVED,
+ &val);
+ if (!ret) {
+ cr2 = readl_relaxed(i2c_dev->base + STM32F7_I2C_CR2);
+ cr2 |= STM32F7_I2C_CR2_NBYTES(1);
+ writel_relaxed(cr2, i2c_dev->base + STM32F7_I2C_CR2);
+ } else {
+ mask = STM32F7_I2C_CR2_NACK;
+ stm32f7_i2c_set_bits(base + STM32F7_I2C_CR2, mask);
+ }
+ }
+
+ /* NACK received */
+ if (status & STM32F7_I2C_ISR_NACKF) {
+ dev_dbg(i2c_dev->dev, "<%s>: Receive NACK\n", __func__);
+ writel_relaxed(STM32F7_I2C_ICR_NACKCF, base + STM32F7_I2C_ICR);
+ }
+
+ /* STOP received */
+ if (status & STM32F7_I2C_ISR_STOPF) {
+ /* Disable interrupts */
+ stm32f7_i2c_disable_irq(i2c_dev, STM32F7_I2C_XFER_IRQ_MASK);
+
+ if (i2c_dev->slave_dir) {
+ /*
+ * Flush TX buffer in order to not used the byte in
+ * TXDR for the next transfer
+ */
+ mask = STM32F7_I2C_ISR_TXE;
+ stm32f7_i2c_set_bits(base + STM32F7_I2C_ISR, mask);
+ }
+
+ /* Clear STOP flag */
+ writel_relaxed(STM32F7_I2C_ICR_STOPCF, base + STM32F7_I2C_ICR);
+
+ /* Notify i2c slave that a STOP flag has been detected */
+ i2c_slave_event(i2c_dev->slave_running, I2C_SLAVE_STOP, &val);
+
+ i2c_dev->slave_running = NULL;
+ }
+
+ /* Address match received */
+ if (status & STM32F7_I2C_ISR_ADDR)
+ stm32f7_i2c_slave_addr(i2c_dev);
+
+ return IRQ_HANDLED;
}
static irqreturn_t stm32f7_i2c_isr_event(int irq, void *data)
struct stm32f7_i2c_msg *f7_msg = &i2c_dev->f7_msg;
void __iomem *base = i2c_dev->base;
u32 status, mask;
+ int ret = IRQ_HANDLED;
+
+ /* Check if the interrupt if for a slave device */
+ if (!i2c_dev->master_mode) {
+ ret = stm32f7_i2c_slave_isr_event(i2c_dev);
+ return ret;
+ }
status = readl_relaxed(i2c_dev->base + STM32F7_I2C_ISR);
/* STOP detection flag */
if (status & STM32F7_I2C_ISR_STOPF) {
/* Disable interrupts */
- stm32f7_i2c_disable_irq(i2c_dev, STM32F7_I2C_ALL_IRQ_MASK);
+ if (stm32f7_i2c_is_slave_registered(i2c_dev))
+ mask = STM32F7_I2C_XFER_IRQ_MASK;
+ else
+ mask = STM32F7_I2C_ALL_IRQ_MASK;
+ stm32f7_i2c_disable_irq(i2c_dev, mask);
/* Clear STOP flag */
writel_relaxed(STM32F7_I2C_ICR_STOPCF, base + STM32F7_I2C_ICR);
- complete(&i2c_dev->complete);
+ if (i2c_dev->use_dma) {
+ ret = IRQ_WAKE_THREAD;
+ } else {
+ i2c_dev->master_mode = false;
+ complete(&i2c_dev->complete);
+ }
}
/* Transfer complete */
if (f7_msg->stop) {
mask = STM32F7_I2C_CR2_STOP;
stm32f7_i2c_set_bits(base + STM32F7_I2C_CR2, mask);
+ } else if (i2c_dev->use_dma) {
+ ret = IRQ_WAKE_THREAD;
+ } else if (f7_msg->smbus) {
+ stm32f7_i2c_smbus_rep_start(i2c_dev);
} else {
i2c_dev->msg_id++;
i2c_dev->msg++;
}
}
+ if (status & STM32F7_I2C_ISR_TCR) {
+ if (f7_msg->smbus)
+ stm32f7_i2c_smbus_reload(i2c_dev);
+ else
+ stm32f7_i2c_reload(i2c_dev);
+ }
+
+ return ret;
+}
+
+static irqreturn_t stm32f7_i2c_isr_event_thread(int irq, void *data)
+{
+ struct stm32f7_i2c_dev *i2c_dev = data;
+ struct stm32f7_i2c_msg *f7_msg = &i2c_dev->f7_msg;
+ struct stm32_i2c_dma *dma = i2c_dev->dma;
+ u32 status;
+ int ret;
+
/*
- * Transfer Complete Reload: 255 data bytes have been transferred
- * We have to prepare the I2C controller to transfer the remaining
- * data.
+ * Wait for dma transfer completion before sending next message or
+ * notity the end of xfer to the client
*/
- if (status & STM32F7_I2C_ISR_TCR)
- stm32f7_i2c_reload(i2c_dev);
+ ret = wait_for_completion_timeout(&i2c_dev->dma->dma_complete, HZ);
+ if (!ret) {
+ dev_dbg(i2c_dev->dev, "<%s>: Timed out\n", __func__);
+ stm32f7_i2c_disable_dma_req(i2c_dev);
+ dmaengine_terminate_all(dma->chan_using);
+ f7_msg->result = -ETIMEDOUT;
+ }
+
+ status = readl_relaxed(i2c_dev->base + STM32F7_I2C_ISR);
+
+ if (status & STM32F7_I2C_ISR_TC) {
+ if (f7_msg->smbus) {
+ stm32f7_i2c_smbus_rep_start(i2c_dev);
+ } else {
+ i2c_dev->msg_id++;
+ i2c_dev->msg++;
+ stm32f7_i2c_xfer_msg(i2c_dev, i2c_dev->msg);
+ }
+ } else {
+ i2c_dev->master_mode = false;
+ complete(&i2c_dev->complete);
+ }
return IRQ_HANDLED;
}
struct stm32f7_i2c_msg *f7_msg = &i2c_dev->f7_msg;
void __iomem *base = i2c_dev->base;
struct device *dev = i2c_dev->dev;
- u32 status;
+ struct stm32_i2c_dma *dma = i2c_dev->dma;
+ u32 mask, status;
status = readl_relaxed(i2c_dev->base + STM32F7_I2C_ISR);
if (status & STM32F7_I2C_ISR_BERR) {
dev_err(dev, "<%s>: Bus error\n", __func__);
writel_relaxed(STM32F7_I2C_ICR_BERRCF, base + STM32F7_I2C_ICR);
+ stm32f7_i2c_release_bus(&i2c_dev->adap);
f7_msg->result = -EIO;
}
f7_msg->result = -EAGAIN;
}
- stm32f7_i2c_disable_irq(i2c_dev, STM32F7_I2C_ALL_IRQ_MASK);
+ if (status & STM32F7_I2C_ISR_PECERR) {
+ dev_err(dev, "<%s>: PEC error in reception\n", __func__);
+ writel_relaxed(STM32F7_I2C_ICR_PECCF, base + STM32F7_I2C_ICR);
+ f7_msg->result = -EINVAL;
+ }
+
+ /* Disable interrupts */
+ if (stm32f7_i2c_is_slave_registered(i2c_dev))
+ mask = STM32F7_I2C_XFER_IRQ_MASK;
+ else
+ mask = STM32F7_I2C_ALL_IRQ_MASK;
+ stm32f7_i2c_disable_irq(i2c_dev, mask);
+
+ /* Disable dma */
+ if (i2c_dev->use_dma) {
+ stm32f7_i2c_disable_dma_req(i2c_dev);
+ dmaengine_terminate_all(dma->chan_using);
+ }
+ i2c_dev->master_mode = false;
complete(&i2c_dev->complete);
return IRQ_HANDLED;
{
struct stm32f7_i2c_dev *i2c_dev = i2c_get_adapdata(i2c_adap);
struct stm32f7_i2c_msg *f7_msg = &i2c_dev->f7_msg;
+ struct stm32_i2c_dma *dma = i2c_dev->dma;
unsigned long time_left;
int ret;
i2c_dev->msg = msgs;
i2c_dev->msg_num = num;
i2c_dev->msg_id = 0;
+ f7_msg->smbus = false;
ret = clk_enable(i2c_dev->clk);
if (ret) {
if (!time_left) {
dev_dbg(i2c_dev->dev, "Access to slave 0x%x timed out\n",
i2c_dev->msg->addr);
+ if (i2c_dev->use_dma)
+ dmaengine_terminate_all(dma->chan_using);
ret = -ETIMEDOUT;
}
return (ret < 0) ? ret : num;
}
+static int stm32f7_i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
+ unsigned short flags, char read_write,
+ u8 command, int size,
+ union i2c_smbus_data *data)
+{
+ struct stm32f7_i2c_dev *i2c_dev = i2c_get_adapdata(adapter);
+ struct stm32f7_i2c_msg *f7_msg = &i2c_dev->f7_msg;
+ struct stm32_i2c_dma *dma = i2c_dev->dma;
+ struct device *dev = i2c_dev->dev;
+ unsigned long timeout;
+ int i, ret;
+
+ f7_msg->addr = addr;
+ f7_msg->size = size;
+ f7_msg->read_write = read_write;
+ f7_msg->smbus = true;
+
+ ret = clk_enable(i2c_dev->clk);
+ if (ret) {
+ dev_err(i2c_dev->dev, "Failed to enable clock\n");
+ return ret;
+ }
+
+ ret = stm32f7_i2c_wait_free_bus(i2c_dev);
+ if (ret)
+ goto clk_free;
+
+ ret = stm32f7_i2c_smbus_xfer_msg(i2c_dev, flags, command, data);
+ if (ret)
+ goto clk_free;
+
+ timeout = wait_for_completion_timeout(&i2c_dev->complete,
+ i2c_dev->adap.timeout);
+ ret = f7_msg->result;
+ if (ret)
+ goto clk_free;
+
+ if (!timeout) {
+ dev_dbg(dev, "Access to slave 0x%x timed out\n", f7_msg->addr);
+ if (i2c_dev->use_dma)
+ dmaengine_terminate_all(dma->chan_using);
+ ret = -ETIMEDOUT;
+ goto clk_free;
+ }
+
+ /* Check PEC */
+ if ((flags & I2C_CLIENT_PEC) && size != I2C_SMBUS_QUICK && read_write) {
+ ret = stm32f7_i2c_smbus_check_pec(i2c_dev);
+ if (ret)
+ goto clk_free;
+ }
+
+ if (read_write && size != I2C_SMBUS_QUICK) {
+ switch (size) {
+ case I2C_SMBUS_BYTE:
+ case I2C_SMBUS_BYTE_DATA:
+ data->byte = f7_msg->smbus_buf[0];
+ break;
+ case I2C_SMBUS_WORD_DATA:
+ case I2C_SMBUS_PROC_CALL:
+ data->word = f7_msg->smbus_buf[0] |
+ (f7_msg->smbus_buf[1] << 8);
+ break;
+ case I2C_SMBUS_BLOCK_DATA:
+ case I2C_SMBUS_BLOCK_PROC_CALL:
+ for (i = 0; i <= f7_msg->smbus_buf[0]; i++)
+ data->block[i] = f7_msg->smbus_buf[i];
+ break;
+ default:
+ dev_err(dev, "Unsupported smbus transaction\n");
+ ret = -EINVAL;
+ }
+ }
+
+clk_free:
+ clk_disable(i2c_dev->clk);
+ return ret;
+}
+
+static int stm32f7_i2c_reg_slave(struct i2c_client *slave)
+{
+ struct stm32f7_i2c_dev *i2c_dev = i2c_get_adapdata(slave->adapter);
+ void __iomem *base = i2c_dev->base;
+ struct device *dev = i2c_dev->dev;
+ u32 oar1, oar2, mask;
+ int id, ret;
+
+ if (slave->flags & I2C_CLIENT_PEC) {
+ dev_err(dev, "SMBus PEC not supported in slave mode\n");
+ return -EINVAL;
+ }
+
+ if (stm32f7_i2c_is_slave_busy(i2c_dev)) {
+ dev_err(dev, "Too much slave registered\n");
+ return -EBUSY;
+ }
+
+ ret = stm32f7_i2c_get_free_slave_id(i2c_dev, slave, &id);
+ if (ret)
+ return ret;
+
+ if (!(stm32f7_i2c_is_slave_registered(i2c_dev))) {
+ ret = clk_enable(i2c_dev->clk);
+ if (ret) {
+ dev_err(dev, "Failed to enable clock\n");
+ return ret;
+ }
+ }
+
+ if (id == 0) {
+ /* Configure Own Address 1 */
+ oar1 = readl_relaxed(i2c_dev->base + STM32F7_I2C_OAR1);
+ oar1 &= ~STM32F7_I2C_OAR1_MASK;
+ if (slave->flags & I2C_CLIENT_TEN) {
+ oar1 |= STM32F7_I2C_OAR1_OA1_10(slave->addr);
+ oar1 |= STM32F7_I2C_OAR1_OA1MODE;
+ } else {
+ oar1 |= STM32F7_I2C_OAR1_OA1_7(slave->addr);
+ }
+ oar1 |= STM32F7_I2C_OAR1_OA1EN;
+ i2c_dev->slave[id] = slave;
+ writel_relaxed(oar1, i2c_dev->base + STM32F7_I2C_OAR1);
+ } else if (id == 1) {
+ /* Configure Own Address 2 */
+ oar2 = readl_relaxed(i2c_dev->base + STM32F7_I2C_OAR2);
+ oar2 &= ~STM32F7_I2C_OAR2_MASK;
+ if (slave->flags & I2C_CLIENT_TEN) {
+ ret = -EOPNOTSUPP;
+ goto exit;
+ }
+
+ oar2 |= STM32F7_I2C_OAR2_OA2_7(slave->addr);
+ oar2 |= STM32F7_I2C_OAR2_OA2EN;
+ i2c_dev->slave[id] = slave;
+ writel_relaxed(oar2, i2c_dev->base + STM32F7_I2C_OAR2);
+ } else {
+ ret = -ENODEV;
+ goto exit;
+ }
+
+ /* Enable ACK */
+ stm32f7_i2c_clr_bits(base + STM32F7_I2C_CR2, STM32F7_I2C_CR2_NACK);
+
+ /* Enable Address match interrupt, error interrupt and enable I2C */
+ mask = STM32F7_I2C_CR1_ADDRIE | STM32F7_I2C_CR1_ERRIE |
+ STM32F7_I2C_CR1_PE;
+ stm32f7_i2c_set_bits(base + STM32F7_I2C_CR1, mask);
+
+ return 0;
+
+exit:
+ if (!(stm32f7_i2c_is_slave_registered(i2c_dev)))
+ clk_disable(i2c_dev->clk);
+
+ return ret;
+}
+
+static int stm32f7_i2c_unreg_slave(struct i2c_client *slave)
+{
+ struct stm32f7_i2c_dev *i2c_dev = i2c_get_adapdata(slave->adapter);
+ void __iomem *base = i2c_dev->base;
+ u32 mask;
+ int id, ret;
+
+ ret = stm32f7_i2c_get_slave_id(i2c_dev, slave, &id);
+ if (ret)
+ return ret;
+
+ WARN_ON(!i2c_dev->slave[id]);
+
+ if (id == 0) {
+ mask = STM32F7_I2C_OAR1_OA1EN;
+ stm32f7_i2c_clr_bits(base + STM32F7_I2C_OAR1, mask);
+ } else {
+ mask = STM32F7_I2C_OAR2_OA2EN;
+ stm32f7_i2c_clr_bits(base + STM32F7_I2C_OAR2, mask);
+ }
+
+ i2c_dev->slave[id] = NULL;
+
+ if (!(stm32f7_i2c_is_slave_registered(i2c_dev))) {
+ stm32f7_i2c_disable_irq(i2c_dev, STM32F7_I2C_ALL_IRQ_MASK);
+ clk_disable(i2c_dev->clk);
+ }
+
+ return 0;
+}
+
static u32 stm32f7_i2c_func(struct i2c_adapter *adap)
{
- return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+ return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR | I2C_FUNC_SLAVE |
+ I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
+ I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
+ I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_BLOCK_PROC_CALL |
+ I2C_FUNC_SMBUS_PROC_CALL | I2C_FUNC_SMBUS_PEC;
}
static struct i2c_algorithm stm32f7_i2c_algo = {
.master_xfer = stm32f7_i2c_xfer,
+ .smbus_xfer = stm32f7_i2c_smbus_xfer,
.functionality = stm32f7_i2c_func,
+ .reg_slave = stm32f7_i2c_reg_slave,
+ .unreg_slave = stm32f7_i2c_unreg_slave,
};
static int stm32f7_i2c_probe(struct platform_device *pdev)
u32 irq_error, irq_event, clk_rate, rise_time, fall_time;
struct i2c_adapter *adap;
struct reset_control *rst;
+ dma_addr_t phy_addr;
int ret;
i2c_dev = devm_kzalloc(&pdev->dev, sizeof(*i2c_dev), GFP_KERNEL);
i2c_dev->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(i2c_dev->base))
return PTR_ERR(i2c_dev->base);
+ phy_addr = (dma_addr_t)res->start;
irq_event = irq_of_parse_and_map(np, 0);
if (!irq_event) {
i2c_dev->dev = &pdev->dev;
- ret = devm_request_irq(&pdev->dev, irq_event, stm32f7_i2c_isr_event, 0,
- pdev->name, i2c_dev);
+ ret = devm_request_threaded_irq(&pdev->dev, irq_event,
+ stm32f7_i2c_isr_event,
+ stm32f7_i2c_isr_event_thread,
+ IRQF_ONESHOT,
+ pdev->name, i2c_dev);
if (ret) {
dev_err(&pdev->dev, "Failed to request irq event %i\n",
irq_event);
init_completion(&i2c_dev->complete);
+ /* Init DMA config if supported */
+ i2c_dev->dma = stm32_i2c_dma_request(i2c_dev->dev, phy_addr,
+ STM32F7_I2C_TXDR,
+ STM32F7_I2C_RXDR);
+
ret = i2c_add_adapter(adap);
if (ret)
goto clk_free;
{
struct stm32f7_i2c_dev *i2c_dev = platform_get_drvdata(pdev);
+ if (i2c_dev->dma) {
+ stm32_i2c_dma_free(i2c_dev->dma);
+ i2c_dev->dma = NULL;
+ }
+
i2c_del_adapter(&i2c_dev->adap);
clk_unprepare(i2c_dev->clk);
u32 val;
int ret;
- if (msg->flags & I2C_M_TEN)
+ if (msg->flags & I2C_M_TEN) {
/* This is probably how 10 bit addresses look */
val = (0xf0 | (((u32) msg->addr & 0x300) >> 7)) &
I2C_DR_D_MASK;
- else
- val = ((msg->addr << 1) & I2C_DR_D_MASK);
+ if (msg->flags & I2C_M_RD)
+ /* This is the direction bit */
+ val |= 0x01;
+ } else {
+ val = i2c_8bit_addr_from_msg(msg);
+ }
- if (msg->flags & I2C_M_RD) {
- /* This is the direction bit */
- val |= 0x01;
- if (resend)
+ if (resend) {
+ if (msg->flags & I2C_M_RD)
dev_dbg(&dev->pdev->dev, "read resend\n");
- } else if (resend)
- dev_dbg(&dev->pdev->dev, "write resend\n");
+ else
+ dev_dbg(&dev->pdev->dev, "write resend\n");
+ }
+
stu300_wr8(val, dev->virtbase + I2C_DR);
/* For 10bit addressing, await 10bit request (EVENT 9) */
dev_dbg(i2c->dev, "calculated timeout %d ms\n", i2c->timeout_ms);
- for (retry = 0; retry < adap->retries; retry++) {
+ for (retry = 0; retry <= adap->retries; retry++) {
ret = synquacer_i2c_doxfer(i2c, msgs, num);
if (ret != -EAGAIN)
return ret;
* @msg_buf_remaining: size of unsent data in the message buffer
* @msg_read: identifies read transfers
* @bus_clk_rate: current i2c bus clock rate
- * @is_suspended: prevents i2c controller accesses after suspend is called
*/
struct tegra_i2c_dev {
struct device *dev;
int msg_read;
u32 bus_clk_rate;
u16 clk_divisor_non_hs_mode;
- bool is_suspended;
bool is_multimaster_mode;
spinlock_t xfer_lock;
};
int i;
int ret = 0;
- if (i2c_dev->is_suspended)
- return -EBUSY;
-
ret = pm_runtime_get_sync(i2c_dev->dev);
if (ret < 0) {
dev_err(i2c_dev->dev, "runtime resume failed %d\n", ret);
}
#ifdef CONFIG_PM_SLEEP
-static int tegra_i2c_suspend(struct device *dev)
-{
- struct tegra_i2c_dev *i2c_dev = dev_get_drvdata(dev);
-
- i2c_lock_adapter(&i2c_dev->adapter);
- i2c_dev->is_suspended = true;
- i2c_unlock_adapter(&i2c_dev->adapter);
-
- return 0;
-}
-
-static int tegra_i2c_resume(struct device *dev)
-{
- struct tegra_i2c_dev *i2c_dev = dev_get_drvdata(dev);
- int ret;
-
- i2c_lock_adapter(&i2c_dev->adapter);
-
- ret = tegra_i2c_init(i2c_dev);
- if (!ret)
- i2c_dev->is_suspended = false;
-
- i2c_unlock_adapter(&i2c_dev->adapter);
-
- return ret;
-}
-
static const struct dev_pm_ops tegra_i2c_pm = {
SET_RUNTIME_PM_OPS(tegra_i2c_runtime_suspend, tegra_i2c_runtime_resume,
NULL)
- SET_SYSTEM_SLEEP_PM_OPS(tegra_i2c_suspend, tegra_i2c_resume)
};
#define TEGRA_I2C_PM (&tegra_i2c_pm)
#else
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/wait.h>
-#include <linux/i2c-xiic.h>
+#include <linux/platform_data/i2c-xiic.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/of.h>
#define XIIC_TX_RX_INTERRUPTS (XIIC_INTR_RX_FULL_MASK | XIIC_TX_INTERRUPTS)
-/* The following constants are used with the following macros to specify the
- * operation, a read or write operation.
- */
-#define XIIC_READ_OPERATION 1
-#define XIIC_WRITE_OPERATION 0
-
/*
* Tx Fifo upper bit masks.
*/
clr |= XIIC_INTR_RX_FULL_MASK;
if (!i2c->rx_msg) {
dev_dbg(i2c->adap.dev.parent,
- "%s unexpexted RX IRQ\n", __func__);
+ "%s unexpected RX IRQ\n", __func__);
xiic_clear_rx_fifo(i2c);
goto out;
}
if (!i2c->tx_msg) {
dev_dbg(i2c->adap.dev.parent,
- "%s unexpexted TX IRQ\n", __func__);
+ "%s unexpected TX IRQ\n", __func__);
goto out;
}
if (!(msg->flags & I2C_M_NOSTART))
/* write the address */
xiic_setreg16(i2c, XIIC_DTR_REG_OFFSET,
- (msg->addr << 1) | XIIC_READ_OPERATION |
- XIIC_TX_DYN_START_MASK);
+ i2c_8bit_addr_from_msg(msg) | XIIC_TX_DYN_START_MASK);
xiic_irq_clr_en(i2c, XIIC_INTR_BNB_MASK);
if (!(msg->flags & I2C_M_NOSTART)) {
/* write the address */
- u16 data = ((msg->addr << 1) & 0xfe) | XIIC_WRITE_OPERATION |
+ u16 data = i2c_8bit_addr_from_msg(msg) |
XIIC_TX_DYN_START_MASK;
if ((i2c->nmsgs == 1) && msg->len == 0)
/* no data and last message -> add STOP */
#include <linux/clk.h>
#include <linux/completion.h>
#include <linux/i2c.h>
+#include <linux/i2c-smbus.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
struct device *dev;
struct i2c_adapter adapter;
struct completion msg_complete;
+ struct i2c_smbus_alert_setup alert_data;
+ struct i2c_client *ara;
int irq;
bool msg_read;
bool len_recv;
priv->msg_buf += len;
}
+static void xlp9xx_i2c_update_rlen(struct xlp9xx_i2c_dev *priv)
+{
+ u32 val, len;
+
+ /*
+ * Update receive length. Re-read len to get the latest value,
+ * and then add 4 to have a minimum value that can be safely
+ * written. This is to account for the byte read above, the
+ * transfer in progress and any delays in the register I/O
+ */
+ val = xlp9xx_read_i2c_reg(priv, XLP9XX_I2C_CTRL);
+ len = xlp9xx_read_i2c_reg(priv, XLP9XX_I2C_FIFOWCNT) &
+ XLP9XX_I2C_FIFO_WCNT_MASK;
+ len = max_t(u32, priv->msg_len, len + 4);
+ if (len >= I2C_SMBUS_BLOCK_MAX + 2)
+ return;
+ val = (val & ~XLP9XX_I2C_CTRL_MCTLEN_MASK) |
+ (len << XLP9XX_I2C_CTRL_MCTLEN_SHIFT);
+ xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_CTRL, val);
+}
+
static void xlp9xx_i2c_drain_rx_fifo(struct xlp9xx_i2c_dev *priv)
{
- u32 len, i, val;
+ u32 len, i;
u8 rlen, *buf = priv->msg_buf;
len = xlp9xx_read_i2c_reg(priv, XLP9XX_I2C_FIFOWCNT) &
if (priv->len_recv) {
/* read length byte */
rlen = xlp9xx_read_i2c_reg(priv, XLP9XX_I2C_MRXFIFO);
- *buf++ = rlen;
- len--;
-
- if (priv->client_pec)
- ++rlen;
- /* update remaining bytes and message length */
- priv->msg_buf_remaining = rlen;
- priv->msg_len = rlen + 1;
+ if (rlen > I2C_SMBUS_BLOCK_MAX || rlen == 0) {
+ rlen = 0; /*abort transfer */
+ priv->msg_buf_remaining = 0;
+ priv->msg_len = 0;
+ } else {
+ *buf++ = rlen;
+ if (priv->client_pec)
+ ++rlen; /* account for error check byte */
+ /* update remaining bytes and message length */
+ priv->msg_buf_remaining = rlen;
+ priv->msg_len = rlen + 1;
+ }
+ xlp9xx_i2c_update_rlen(priv);
priv->len_recv = false;
-
- /* Update transfer length to read only actual data */
- val = xlp9xx_read_i2c_reg(priv, XLP9XX_I2C_CTRL);
- val = (val & ~XLP9XX_I2C_CTRL_MCTLEN_MASK) |
- ((rlen + 1) << XLP9XX_I2C_CTRL_MCTLEN_SHIFT);
- xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_CTRL, val);
} else {
len = min(priv->msg_buf_remaining, len);
for (i = 0; i < len; i++, buf++)
xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_MFIFOCTRL,
XLP9XX_I2C_MFIFOCTRL_RST);
- /* set FIFO threshold if reading */
- if (priv->msg_read)
- xlp9xx_i2c_update_rx_fifo_thres(priv);
-
/* set slave addr */
xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_SLAVEADDR,
(msg->addr << XLP9XX_I2C_SLAVEADDR_ADDR_SHIFT) |
val &= ~XLP9XX_I2C_CTRL_ADDMODE;
priv->len_recv = msg->flags & I2C_M_RECV_LEN;
- len = priv->len_recv ? XLP9XX_I2C_FIFO_SIZE : msg->len;
+ len = priv->len_recv ? I2C_SMBUS_BLOCK_MAX + 2 : msg->len;
priv->client_pec = msg->flags & I2C_CLIENT_PEC;
+ /* set FIFO threshold if reading */
+ if (priv->msg_read)
+ xlp9xx_i2c_update_rx_fifo_thres(priv);
+
/* set data length to be transferred */
val = (val & ~XLP9XX_I2C_CTRL_MCTLEN_MASK) |
(len << XLP9XX_I2C_CTRL_MCTLEN_SHIFT);
}
/* update msg->len with actual received length */
- if (msg->flags & I2C_M_RECV_LEN)
+ if (msg->flags & I2C_M_RECV_LEN) {
+ if (!priv->msg_len)
+ return -EPROTO;
msg->len = priv->msg_len;
+ }
return 0;
}
return 0;
}
+static int xlp9xx_i2c_smbus_setup(struct xlp9xx_i2c_dev *priv,
+ struct platform_device *pdev)
+{
+ if (!priv->alert_data.irq)
+ return -EINVAL;
+
+ priv->ara = i2c_setup_smbus_alert(&priv->adapter, &priv->alert_data);
+ if (!priv->ara)
+ return -ENODEV;
+
+ return 0;
+}
+
static int xlp9xx_i2c_probe(struct platform_device *pdev)
{
struct xlp9xx_i2c_dev *priv;
dev_err(&pdev->dev, "invalid irq!\n");
return priv->irq;
}
+ /* SMBAlert irq */
+ priv->alert_data.irq = platform_get_irq(pdev, 1);
+ if (priv->alert_data.irq <= 0)
+ priv->alert_data.irq = 0;
xlp9xx_i2c_get_frequency(pdev, priv);
xlp9xx_i2c_init(priv);
if (err)
return err;
+ err = xlp9xx_i2c_smbus_setup(priv, pdev);
+ if (err)
+ dev_dbg(&pdev->dev, "No active SMBus alert %d\n", err);
+
platform_set_drvdata(pdev, priv);
dev_dbg(&pdev->dev, "I2C bus:%d added\n", priv->adapter.nr);
client->adapter = adap;
client->dev.platform_data = info->platform_data;
-
- if (info->archdata)
- client->dev.archdata = *info->archdata;
-
client->flags = info->flags;
client->addr = info->addr;
client->dev.parent = &client->adapter->dev;
client->dev.bus = &i2c_bus_type;
client->dev.type = &i2c_client_type;
- client->dev.of_node = info->of_node;
+ client->dev.of_node = of_node_get(info->of_node);
client->dev.fwnode = info->fwnode;
i2c_dev_set_name(adap, client, info);
dev_err(&adap->dev,
"Failed to add properties to client %s: %d\n",
client->name, status);
- goto out_err;
+ goto out_err_put_of_node;
}
}
out_free_props:
if (info->properties)
device_remove_properties(&client->dev);
+out_err_put_of_node:
+ of_node_put(info->of_node);
out_err:
dev_err(&adap->dev,
"Failed to register i2c client %s at 0x%02x (%d)\n",
#include "i2c-core.h"
-static struct i2c_client *of_i2c_register_device(struct i2c_adapter *adap,
- struct device_node *node)
+int of_i2c_get_board_info(struct device *dev, struct device_node *node,
+ struct i2c_board_info *info)
{
- struct i2c_client *client;
- struct i2c_board_info info = {};
- struct dev_archdata dev_ad = {};
u32 addr;
int ret;
- dev_dbg(&adap->dev, "of_i2c: register %pOF\n", node);
+ memset(info, 0, sizeof(*info));
- if (of_modalias_node(node, info.type, sizeof(info.type)) < 0) {
- dev_err(&adap->dev, "of_i2c: modalias failure on %pOF\n",
- node);
- return ERR_PTR(-EINVAL);
+ if (of_modalias_node(node, info->type, sizeof(info->type)) < 0) {
+ dev_err(dev, "of_i2c: modalias failure on %pOF\n", node);
+ return -EINVAL;
}
ret = of_property_read_u32(node, "reg", &addr);
if (ret) {
- dev_err(&adap->dev, "of_i2c: invalid reg on %pOF\n", node);
- return ERR_PTR(ret);
+ dev_err(dev, "of_i2c: invalid reg on %pOF\n", node);
+ return ret;
}
if (addr & I2C_TEN_BIT_ADDRESS) {
addr &= ~I2C_TEN_BIT_ADDRESS;
- info.flags |= I2C_CLIENT_TEN;
+ info->flags |= I2C_CLIENT_TEN;
}
if (addr & I2C_OWN_SLAVE_ADDRESS) {
addr &= ~I2C_OWN_SLAVE_ADDRESS;
- info.flags |= I2C_CLIENT_SLAVE;
+ info->flags |= I2C_CLIENT_SLAVE;
}
- info.addr = addr;
- info.archdata = &dev_ad;
- info.of_node = of_node_get(node);
+ info->addr = addr;
+ info->of_node = node;
if (of_property_read_bool(node, "host-notify"))
- info.flags |= I2C_CLIENT_HOST_NOTIFY;
+ info->flags |= I2C_CLIENT_HOST_NOTIFY;
if (of_get_property(node, "wakeup-source", NULL))
- info.flags |= I2C_CLIENT_WAKE;
+ info->flags |= I2C_CLIENT_WAKE;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(of_i2c_get_board_info);
+
+static struct i2c_client *of_i2c_register_device(struct i2c_adapter *adap,
+ struct device_node *node)
+{
+ struct i2c_client *client;
+ struct i2c_board_info info;
+ int ret;
+
+ dev_dbg(&adap->dev, "of_i2c: register %pOF\n", node);
+
+ ret = of_i2c_get_board_info(&adap->dev, node, &info);
+ if (ret)
+ return ERR_PTR(ret);
client = i2c_new_device(adap, &info);
if (!client) {
dev_err(&adap->dev, "of_i2c: Failure registering %pOF\n", node);
- of_node_put(node);
return ERR_PTR(-EINVAL);
}
return client;
status = i2c_transfer(adapter, msg, num);
if (status < 0)
return status;
+ if (status != num)
+ return -EIO;
/* Check PEC if last message is a read */
if (i && (msg[num-1].flags & I2C_M_RD)) {
snprintf(symlink_name, sizeof(symlink_name), "channel-%u", chan_id);
WARN(sysfs_create_link(&muxc->dev->kobj, &priv->adap.dev.kobj,
symlink_name),
- "can't create symlink for channel %u\n", chan_id);
+ "can't create symlink to channel %u\n", chan_id);
dev_info(&parent->dev, "Added multiplexed i2c bus %d\n",
i2c_adapter_id(&priv->adap));
#include <linux/of.h>
#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
priv->cur_adap.owner = THIS_MODULE;
priv->cur_adap.algo = &priv->algo;
priv->cur_adap.algo_data = priv;
- priv->cur_adap.dev.parent = priv->dev;
+ priv->cur_adap.dev.parent = &adap->dev;
priv->cur_adap.class = adap->class;
priv->cur_adap.retries = adap->retries;
priv->cur_adap.timeout = adap->timeout;
platform_set_drvdata(pdev, priv);
+ pm_runtime_no_callbacks(&pdev->dev);
+
/* switch to first parent as active master */
i2c_demux_activate_master(priv, 0);
#include <linux/i2c.h>
#include <linux/i2c-mux.h>
-#include <linux/i2c-mux-gpio.h>
+#include <linux/platform_data/i2c-mux-gpio.h>
#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/slab.h>
};
MODULE_DEVICE_TABLE(of, ltc4306_of_match);
-static int ltc4306_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int ltc4306_probe(struct i2c_client *client)
{
struct i2c_adapter *adap = to_i2c_adapter(client->dev.parent);
const struct chip_desc *chip;
chip = of_device_get_match_data(&client->dev);
if (!chip)
- chip = &chips[id->driver_data];
+ chip = &chips[i2c_match_id(ltc4306_id, client)->driver_data];
idle_disc = device_property_read_bool(&client->dev,
"i2c-mux-idle-disconnect");
.name = "ltc4306",
.of_match_table = of_match_ptr(ltc4306_of_match),
},
- .probe = ltc4306_probe,
+ .probe_new = ltc4306_probe,
.remove = ltc4306_remove,
.id_table = ltc4306_id,
};
*/
#include <linux/device.h>
+#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/i2c-mux.h>
int num, force, class;
struct i2c_mux_core *muxc;
struct pca954x *data;
- const struct of_device_id *match;
int ret;
if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_BYTE))
i2c_set_clientdata(client, muxc);
data->client = client;
- /* Get the mux out of reset if a reset GPIO is specified. */
- gpio = devm_gpiod_get_optional(&client->dev, "reset", GPIOD_OUT_LOW);
+ /* Reset the mux if a reset GPIO is specified. */
+ gpio = devm_gpiod_get_optional(&client->dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(gpio))
return PTR_ERR(gpio);
+ if (gpio) {
+ udelay(1);
+ gpiod_set_value_cansleep(gpio, 0);
+ /* Give the chip some time to recover. */
+ udelay(1);
+ }
- match = of_match_device(of_match_ptr(pca954x_of_match), &client->dev);
- if (match)
- data->chip = of_device_get_match_data(&client->dev);
- else
+ data->chip = of_device_get_match_data(&client->dev);
+ if (!data->chip)
data->chip = &chips[id->driver_data];
if (data->chip->id.manufacturer_id != I2C_DEVICE_ID_NONE) {
values = devm_kcalloc(&pdev->dev,
mux->data.n_values, sizeof(*mux->data.values),
GFP_KERNEL);
- if (!values) {
- dev_err(&pdev->dev, "Cannot allocate values array");
+ if (!values)
return -ENOMEM;
- }
for_each_child_of_node(np, child) {
of_property_read_u32(child, "reg", values + i);
Say N here if you have a headless (no monitor, no keyboard) system.
- More information is available: <file:Documentation/input/input.txt>
+ More information is available: <file:Documentation/input/input.rst>
If unsure, say Y.
If unsure, say Y.
- More information is available: <file:Documentation/input/joystick.txt>
+ More information is available: <file:Documentation/input/joydev/joystick.rst>
To compile this driver as a module, choose M here: the
module will be called joydev.
and the list of supported devices will be displayed. This option
doesn't affect the kernel.
- Please read the file <file:Documentation/input/joystick.txt> which
+ Please read the file <file:Documentation/input/joydev/joystick.rst> which
contains more information.
if INPUT_JOYSTICK
Flightstick Pro, ThrustMaster FCS, 6 and 8 button gamepads, or
Saitek Cyborg joysticks.
- Please read the file <file:Documentation/input/joystick.txt> which
+ Please read the file <file:Documentation/input/joydev/joystick.rst> which
contains more information.
To compile this driver as a module, choose M here: the
gamepad, Sega Saturn gamepad, or a Multisystem -- Atari, Amiga,
Commodore, Amstrad CPC joystick connected to your parallel port.
For more information on how to use the driver please read
- <file:Documentation/input/joystick-parport.txt>.
+ <file:Documentation/input/devices/joystick-parport.rst>.
To compile this driver as a module, choose M here: the
module will be called db9.
Sony PlayStation gamepad or a Multisystem -- Atari, Amiga,
Commodore, Amstrad CPC joystick connected to your parallel port.
For more information on how to use the driver please read
- <file:Documentation/input/joystick-parport.txt>.
+ <file:Documentation/input/devices/joystick-parport.rst>.
To compile this driver as a module, choose M here: the
module will be called gamecon.
Say Y here if you have the TurboGraFX interface by Steffen Schwenke,
and want to use it with Multisystem -- Atari, Amiga, Commodore,
Amstrad CPC joystick. For more information on how to use the driver
- please read <file:Documentation/input/joystick-parport.txt>.
+ please read <file:Documentation/input/devices/joystick-parport.rst>.
To compile this driver as a module, choose M here: the
module will be called turbografx.
and/or "Event interface support" (CONFIG_INPUT_EVDEV) as well.
For information about how to connect the X-Box pad to USB, see
- <file:Documentation/input/xpad.txt>.
+ <file:Documentation/input/devices/xpad.rst>.
To compile this driver as a module, choose M here: the
module will be called xpad.
Say Y or M here if you have a Walkera WK-0701 transmitter which is
supplied with a ready to fly Walkera helicopters such as HM36,
HM37, HM60 and want to use it via parport as a joystick. More
- information is available: <file:Documentation/input/walkera0701.txt>
+ information is available: <file:Documentation/input/devices/walkera0701.rst>
To compile this driver as a module, choose M here: the
module will be called walkera0701.
connected to your serial (COM) port.
You will need an additional utility called inputattach, see
- <file:Documentation/input/joystick.txt>
- and <file:Documentation/input/ff.txt>.
+ <file:Documentation/input/joydev/joystick.rst>
+ and <file:Documentation/input/ff.rst>.
*
* Copyright (c) 2008 Peter Popovec
*
- * More about driver: <file:Documentation/input/walkera0701.txt>
+ * More about driver: <file:Documentation/input/devices/walkera0701.rst>
*/
/*
usb sound driver, so you might want to enable that as well.
For information about how to use these additional functions, see
- <file:Documentation/input/yealink.txt>.
+ <file:Documentation/input/devices/yealink.rst>.
To compile this driver as a module, choose M here: the module will be
called yealink.
depends on GPIOLIB || COMPILE_TEST
help
Say Y here to add support for rotary encoders connected to GPIO lines.
- Check file:Documentation/input/rotary-encoder.txt for more
+ Check file:Documentation/input/devices/rotary-encoder.rst for more
information.
To compile this driver as a module, choose M here: the
* state machine code inspired by code from Tim Ruetz
*
* A generic driver for rotary encoders connected to GPIO lines.
- * See file:Documentation/input/rotary-encoder.txt for more information
+ * See file:Documentation/input/devices/rotary-encoder.rst for more information
*
* 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
This driver exposes some configuration registers via sysfs
entries. For further information,
- see <file:Documentation/input/elantech.txt>.
+ see <file:Documentation/input/devices/elantech.rst>.
If unsure, say N.
scrolling in X11.
For further information, see
- <file:Documentation/input/appletouch.txt>.
+ <file:Documentation/input/devices/appletouch.rst>.
To compile this driver as a module, choose M here: the
module will be called appletouch.
The interface is currently identical to the appletouch interface,
for further information, see
- <file:Documentation/input/appletouch.txt>.
+ <file:Documentation/input/devices/appletouch.rst>.
To compile this driver as a module, choose M here: the
module will be called bcm5974.
static void alps_set_abs_params_ss4_v2(struct alps_data *priv,
struct input_dev *dev1);
-/* Packet formats are described in Documentation/input/alps.txt */
+/* Packet formats are described in Documentation/input/devices/alps.rst */
static bool alps_is_valid_first_byte(struct alps_data *priv,
unsigned char data)
Say Y here if you plan to use an input device (mouse, joystick,
tablet, 6dof) that communicates over the RS232 serial (COM) port.
- More information is available: <file:Documentation/input/input.txt>
+ More information is available: <file:Documentation/input/input.rst>
If unsure, say Y.
Say Y here if you built a simple parallel port adapter to attach
an additional AT keyboard, XT keyboard or PS/2 mouse.
- More information is available: <file:Documentation/input/input.txt>
+ More information is available: <file:Documentation/input/input.rst>
If unsure, say N.
* The default values correspond to Mainstone II in QVGA mode
*
* Please read
- * Documentation/input/input-programming.txt for more details.
+ * Documentation/input/input-programming.rst for more details.
*/
static int abs_x[3] = {150, 4000, 5};
/*
* Initialize ring buffer. The data and metadata buffers must be previously
* allocated and their size must be a power of two
- * (Documentation/circular-buffers.txt)
+ * (Documentation/core-api/circular-buffers.rst)
*/
int pblk_rb_init(struct pblk_rb *rb, struct pblk_rb_entry *rb_entry_base,
unsigned int power_size, unsigned int power_seg_sz)
Allows a block device to be used as cache for other devices; uses
a btree for indexing and the layout is optimized for SSDs.
- See Documentation/bcache.txt for details.
+ See Documentation/admin-guide/bcache.rst for details.
config BCACHE_DEBUG
bool "Bcache debugging"
* as keys are inserted we only sort the pages that have not yet been written.
* When garbage collection is run, we resort the entire node.
*
- * All configuration is done via sysfs; see Documentation/bcache.txt.
+ * All configuration is done via sysfs; see Documentation/admin-guide/bcache.rst.
*/
#include "bcache.h"
* as keys are inserted we only sort the pages that have not yet been written.
* When garbage collection is run, we resort the entire node.
*
- * All configuration is done via sysfs; see Documentation/bcache.txt.
+ * All configuration is done via sysfs; see Documentation/admin-guide/bcache.rst.
*/
#include "bcache.h"
* this pairs with smp_store_release() in dvb_ringbuffer_write(),
* dvb_ringbuffer_write_user(), or dvb_ringbuffer_reset()
*
- * for memory barriers also see Documentation/circular-buffers.txt
+ * for memory barriers also see Documentation/core-api/circular-buffers.rst
*/
return (rbuf->pread == smp_load_acquire(&rbuf->pwrite));
}
A DVB-T tuner module. Say Y when you want to support this frontend.
This driver needs external firmware. Please use the command
- "<kerneldir>/Documentation/dvb/get_dvb_firmware sp8870" to
+ "<kerneldir>/scripts/get_dvb_firmware sp8870" to
download/extract it, and then copy it to /usr/lib/hotplug/firmware
or /lib/firmware (depending on configuration of firmware hotplug).
A DVB-T tuner module. Say Y when you want to support this frontend.
This driver needs external firmware. Please use the command
- "<kerneldir>/Documentation/dvb/get_dvb_firmware sp887x" to
+ "<kerneldir>/scripts/get_dvb_firmware sp887x" to
download/extract it, and then copy it to /usr/lib/hotplug/firmware
or /lib/firmware (depending on configuration of firmware hotplug).
A DVB-T tuner module. Say Y when you want to support this frontend.
This driver needs external firmware. Please use the commands
- "<kerneldir>/Documentation/dvb/get_dvb_firmware tda10045",
- "<kerneldir>/Documentation/dvb/get_dvb_firmware tda10046" to
+ "<kerneldir>/scripts/get_dvb_firmware tda10045",
+ "<kerneldir>/scripts/get_dvb_firmware tda10046" to
download/extract them, and then copy them to /usr/lib/hotplug/firmware
or /lib/firmware (depending on configuration of firmware hotplug).
to support this frontend.
This driver needs external firmware. Please use the commands
- "<kerneldir>/Documentation/dvb/get_dvb_firmware nxt2002" and
- "<kerneldir>/Documentation/dvb/get_dvb_firmware nxt2004" to
+ "<kerneldir>/scripts/get_dvb_firmware nxt2002" and
+ "<kerneldir>/scripts/get_dvb_firmware nxt2004" to
download/extract them, and then copy them to /usr/lib/hotplug/firmware
or /lib/firmware (depending on configuration of firmware hotplug).
An ATSC 8VSB tuner module. Say Y when you want to support this frontend.
This driver needs external firmware. Please use the command
- "<kerneldir>/Documentation/dvb/get_dvb_firmware or51211" to
+ "<kerneldir>/scripts/get_dvb_firmware or51211" to
download it, and then copy it to /usr/lib/hotplug/firmware
or /lib/firmware (depending on configuration of firmware hotplug).
to support this frontend.
This driver needs external firmware. Please use the commands
- "<kerneldir>/Documentation/dvb/get_dvb_firmware or51132_vsb" and/or
- "<kerneldir>/Documentation/dvb/get_dvb_firmware or51132_qam" to
+ "<kerneldir>/scripts/get_dvb_firmware or51132_vsb" and/or
+ "<kerneldir>/scripts/get_dvb_firmware or51132_qam" to
download firmwares for 8VSB and QAM64/256, respectively. Copy them to
/usr/lib/hotplug/firmware or /lib/firmware (depending on
configuration of firmware hotplug).
* Amaury Demol from DiBcom for providing specs and driver
* sources, on which this driver (and the dvb-dibusb) are based.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*
*/
* Amaury Demol from DiBcom for providing specs and driver
* sources, on which this driver (and the dvb-dibusb) are based.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*
*/
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, version 2.
*
-* see Documentation/dvb/README.dvb-usb for more information
+* see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#ifndef EDS1547
* ATI HDTV Wonder (NXT2004)
*
* This driver needs external firmware. Please use the command
- * "<kerneldir>/Documentation/dvb/get_dvb_firmware nxt2002" or
- * "<kerneldir>/Documentation/dvb/get_dvb_firmware nxt2004" to
+ * "<kerneldir>/scripts/get_dvb_firmware nxt2002" or
+ * "<kerneldir>/scripts/get_dvb_firmware nxt2004" to
* download/extract the appropriate firmware, and then copy it to
* /usr/lib/hotplug/firmware/ or /lib/firmware/
* (depending on configuration of firmware hotplug).
/*
* This driver needs external firmware. Please use the command
- * "<kerneldir>/Documentation/dvb/get_dvb_firmware or51211" to
+ * "<kerneldir>/scripts/get_dvb_firmware or51211" to
* download/extract it, and then copy it to /usr/lib/hotplug/firmware
* or /lib/firmware (depending on configuration of firmware hotplug).
*/
*/
/*
* This driver needs external firmware. Please use the command
- * "<kerneldir>/Documentation/dvb/get_dvb_firmware alps_tdlb7" to
+ * "<kerneldir>/scripts/get_dvb_firmware alps_tdlb7" to
* download/extract it, and then copy it to /usr/lib/hotplug/firmware
* or /lib/firmware (depending on configuration of firmware hotplug).
*/
/*
* This driver needs external firmware. Please use the command
- * "<kerneldir>/Documentation/dvb/get_dvb_firmware sp887x" to
+ * "<kerneldir>/scripts/get_dvb_firmware sp887x" to
* download/extract it, and then copy it to /usr/lib/hotplug/firmware
* or /lib/firmware (depending on configuration of firmware hotplug).
*/
*/
/*
* This driver needs external firmware. Please use the commands
- * "<kerneldir>/Documentation/dvb/get_dvb_firmware tda10045",
- * "<kerneldir>/Documentation/dvb/get_dvb_firmware tda10046" to
+ * "<kerneldir>/scripts/get_dvb_firmware tda10045",
+ * "<kerneldir>/scripts/get_dvb_firmware tda10046" to
* download/extract them, and then copy them to /usr/lib/hotplug/firmware
* or /lib/firmware (depending on configuration of firmware hotplug).
*/
ret = request_firmware(&fw, fw_file, &client->dev);
if (ret) {
dev_err(&client->dev,
- "did not find the firmware file. (%s) Please see linux/Documentation/dvb/ for more details on firmware-problems. (%d)\n",
+ "did not find the firmware file '%s' (status %d). You can use <kernel_dir>/scripts/get_dvb_firmware to get the firmware\n",
fw_file, ret);
goto error;
}
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, version 2.
*
-* see Documentation/dvb/README.dvb-usb for more information
+* see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#ifndef Z0194A
/*
* I2S output enable/disable configuration. This is a private control.
- * Refer to Documentation/media/v4l-drivers/max2175 for more details.
+ * Refer to Documentation/media/v4l-drivers/max2175.rst for more details.
*/
static const struct v4l2_ctrl_config max2175_i2s_en = {
.ops = &max2175_ctrl_ops,
/*
* HSLS value control LO freq adjacent location configuration.
- * Refer to Documentation/media/v4l-drivers/max2175 for more details.
+ * Refer to Documentation/media/v4l-drivers/max2175.rst for more details.
*/
static const struct v4l2_ctrl_config max2175_hsls = {
.ops = &max2175_ctrl_ops,
/*
* Rx modes below are a set of preset configurations that decides the tuner's
* sck and sample rate of transmission. They are separate for EU & NA regions.
- * Refer to Documentation/media/v4l-drivers/max2175 for more details.
+ * Refer to Documentation/media/v4l-drivers/max2175.rst for more details.
*/
static const char * const max2175_ctrl_eu_rx_modes[] = {
[MAX2175_EU_FM_1_2] = "EU FM 1.2",
---help---
Support for BT848 based frame grabber/overlay boards. This includes
the Miro, Hauppauge and STB boards. Please read the material in
- <file:Documentation/video4linux/bttv/> for more information.
+ <file:Documentation/media/v4l-drivers/bttv.rst> for more information.
To compile this driver as a module, choose M here: the
module will be called bttv.
if (ret) {
CX18_ERR("The MPC718 board variant with the MT352 DVB-T demodulator will not work without it\n");
- CX18_ERR("Run 'linux/Documentation/dvb/get_dvb_firmware mpc718' if you need the firmware\n");
+ CX18_ERR("Run 'linux/scripts/get_dvb_firmware mpc718' if you need the firmware\n");
}
return ret;
}
/*
* Audio related reset according to
- * Documentation/video4linux/cx2341x/fw-encoder-api.txt
+ * Documentation/media/v4l-drivers/cx2341x.rst
*/
if (atomic_read(&cx->ana_capturing) == 0)
cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 2,
/*
* Number of lines for Field 1 & Field 2 according to
- * Documentation/video4linux/cx2341x/fw-encoder-api.txt
+ * Documentation/media/v4l-drivers/cx2341x.rst
* Field 1 is 312 for 625 line systems in BT.656
* Field 2 is 313 for 625 line systems in BT.656
*/
ret = request_firmware(&fw, filename, &dev->pci->dev);
if (ret != 0)
- pr_err("did not find the firmware file. (%s) Please see linux/Documentation/dvb/ for more details on firmware-problems.",
+ pr_err("did not find the firmware file '%s'. You can use <kernel_dir>/scripts/get_dvb_firmware to get the firmware.",
filename);
else
altera_init(&netup_config, fw);
---help---
This is the video4linux driver for the Motion Eye camera found
in the Vaio Picturebook laptops. Please read the material in
- <file:Documentation/video4linux/meye.txt> for more information.
+ <file:Documentation/media/v4l-drivers/meye.rst> for more information.
If you say Y or M here, you need to say Y or M to "Sony Laptop
Extras" in the misc device section.
onboard MPEG2 decoder.
This driver needs an external firmware. Please use the script
- "<kerneldir>/Documentation/dvb/get_dvb_firmware av7110" to
+ "<kerneldir>/scripts/get_dvb_firmware av7110" to
download/extract it, and then copy it to /usr/lib/hotplug/firmware
or /lib/firmware (depending on configuration of firmware hotplug).
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/i2c.h>
-#include <linux/i2c-gpio.h>
+#include <linux/platform_data/i2c-gpio.h>
#include <linux/interrupt.h>
#include <linux/spinlock.h>
#include <linux/slab.h>
* - a videobuffer is queued on the pcdev->capture list
*
* Please check the "DMA hot chaining timeslice issue" in
- * Documentation/video4linux/pxa_camera.txt
+ * Documentation/media/v4l-drivers/pxa_camera.rst
*
* Context: should only be called within the dma irq handler
*/
/*
* Please check the DMA prepared buffer structure in :
- * Documentation/video4linux/pxa_camera.txt
+ * Documentation/media/v4l-drivers/pxa_camera.rst
* Please check also in pxa_camera_check_link_miss() to understand why DMA chain
* modification while DMA chain is running will work anyway.
*/
/*
* CEU can scale and crop, but we don't want to waste bandwidth and kill the
* framerate by always requesting the maximum image from the client. See
- * Documentation/video4linux/sh_mobile_ceu_camera.txt for a description of
+ * Documentation/media/v4l-drivers/sh_mobile_ceu_camera.rst for a description of
* scaling and cropping algorithms and for the meaning of referenced here steps.
*/
static int sh_mobile_ceu_set_selection(struct soc_camera_device *icd,
#include <linux/via-core.h>
#include <linux/via-gpio.h>
#include <linux/via_i2c.h>
+
+#ifdef CONFIG_X86
#include <asm/olpc.h>
+#else
+#define machine_is_olpc(x) 0
+#endif
#include "via-camera.h"
In order to control your radio card, you will need to use programs
that are compatible with the Video For Linux 2 API. Information on
this API and pointers to "v4l2" programs may be found at
- <file:Documentation/video4linux/API.html>.
+ <file:Documentation/media/media_uapi.rst>.
To compile this driver as a module, choose M here: the
module will be called radio-si476x.
In order to control your radio card, you will need to use programs
that are compatible with the Video For Linux API. Information on
this API and pointers to "v4l" programs may be found at
- <file:Documentation/video4linux/API.html>.
+ <file:Documentation/media/media_uapi.rst>.
To compile this driver as a module, choose M here: the
module will be called radio-maxiradio.
In order to control your radio card, you will need to use programs
that are compatible with the Video For Linux API. Information on
this API and pointers to "v4l" programs may be found at
- <file:Documentation/video4linux/API.html>.
+ <file:Documentation/media/media_uapi.rst>.
To compile this driver as a module, choose M here: the
module will be called radio-shark.
In order to control your radio card, you will need to use programs
that are compatible with the Video For Linux API. Information on
this API and pointers to "v4l" programs may be found at
- <file:Documentation/video4linux/API.html>.
+ <file:Documentation/media/media_uapi.rst>.
To compile this driver as a module, choose M here: the
module will be called radio-shark2.
In order to control your radio card, you will need to use programs
that are compatible with the Video For Linux 2 API. Information on
this API and pointers to "v4l2" programs may be found at
- <file:Documentation/video4linux/API.html>.
+ <file:Documentation/media/media_uapi.rst>.
To compile this driver as a module, choose M here: the
module will be called radio-wl1273.
been reported to be used by these cards.
More information is contained in the file
- <file:Documentation/video4linux/radiotrack.txt>.
+ <file:Documentation/media/v4l-drivers/radiotrack.rst>.
To compile this driver as a module, choose M here: the
module will be called radio-aimslab.
Please have a look at the documentation, especially on how
to redirect the audio stream from the radio to your sound device:
- Documentation/video4linux/si470x.txt
+ Documentation/media/v4l-drivers/si470x.rst
Say Y here if you want to connect this type of radio to your
computer's USB port.
In order to control your radio card, you will need to use programs
that are compatible with the Video For Linux 2 API. Information on
this API and pointers to "v4l2" programs may be found at
- <file:Documentation/video4linux/API.html>.
+ <file:Documentation/media/media_uapi.rst>.
endmenu
USB1.1 and USB2.0 DVB devices.
Almost every USB device needs a firmware, please look into
- <file:Documentation/dvb/README.dvb-usb>.
+ <file:Documentation/media/dvb-drivers/dvb-usb.rst>.
For a complete list of supported USB devices see the LinuxTV DVB Wiki:
<https://linuxtv.org/wiki/index.php/DVB_USB>
ret = request_firmware(&fw, name, &d->udev->dev);
if (ret < 0) {
dev_err(&d->udev->dev,
- "%s: Did not find the firmware file '%s'. Please see linux/Documentation/dvb/ for more details on firmware-problems. Status %d\n",
+ "%s: Did not find the firmware file '%s' (status %d). You can use <kernel_dir>/scripts/get_dvb_firmware to get the firmware\n",
KBUILD_MODNAME, name, ret);
goto err;
}
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, version 2.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#include "gl861.h"
*
* LME2510C + M88RS2000
*
- * For firmware see Documentation/dvb/lmedm04.txt
+ * For firmware see Documentation/media/dvb-drivers/lmedm04.rst
*
* I2C addresses:
* 0xd0 - STV0288 - Demodulator
* GNU General Public License for more details.
*
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*
* Known Issues :
* LME2510: Non Intel USB chipsets fail to maintain High Speed on
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 2.
* *
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#ifndef _DVB_USB_LME2510_H_
#define _DVB_USB_LME2510_H_
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 2.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#include <linux/vmalloc.h>
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 2.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#ifndef _DVB_USB_MXL111SF_H_
USB1.1 and USB2.0 DVB devices.
Almost every USB device needs a firmware, please look into
- <file:Documentation/dvb/README.dvb-usb>.
+ <file:Documentation/media/dvb-drivers/dvb-usb.rst>.
For a complete list of supported USB devices see the LinuxTV DVB Wiki:
<https://linuxtv.org/wiki/index.php/DVB_USB>
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 2.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#include "dibusb.h"
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#include "af9005.h"
#include "af9005-script.h"
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#include "af9005.h"
/* debug */
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#include "af9005.h"
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#ifndef _DVB_USB_AF9005_H_
#define _DVB_USB_AF9005_H_
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 2.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#include "az6027.h"
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 2.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#include <media/tuner.h>
#include <linux/vmalloc.h>
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 2.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#include "dibusb.h"
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 2.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#include "dibusb.h"
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 2.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#include "dibusb.h"
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 2.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#include "dibusb.h"
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 2.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#ifndef _DVB_USB_DIBUSB_H_
#define _DVB_USB_DIBUSB_H_
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 2.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#include "digitv.h"
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 2.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#include "dtt200u.h"
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 2.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#include "dtt200u.h"
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 2.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#ifndef _DVB_USB_DTT200U_H_
#define _DVB_USB_DTT200U_H_
const struct firmware *fw = NULL;
if ((ret = request_firmware(&fw, props->firmware, &udev->dev)) != 0) {
- err("did not find the firmware file. (%s) Please see linux/Documentation/dvb/ for more details on firmware-problems. (%d)",
+ err("did not find the firmware file '%s' (status %d). You can use <kernel_dir>/scripts/get_dvb_firmware to get the firmware",
props->firmware,ret);
return ret;
}
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 2.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#include "dvb-usb-common.h"
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, version 2.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#include <media/dvb-usb-ids.h>
#include "dw2102.h"
#define P1100_FIRMWARE "dvb-usb-p1100.fw"
#define P7500_FIRMWARE "dvb-usb-p7500.fw"
-#define err_str "did not find the firmware file. (%s) " \
- "Please see linux/Documentation/dvb/ for more details " \
- "on firmware-problems."
+#define err_str "did not find the firmware file '%s'. You can use <kernel_dir>/scripts/get_dvb_firmware to get the firmware"
struct dw2102_state {
u8 initialized;
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 2.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#include <linux/init.h>
#include <linux/string.h>
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 2.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#include "friio.h"
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 2.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#ifndef _DVB_USB_FRIIO_H_
#define _DVB_USB_FRIIO_H_
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 2.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#include "gp8psk.h"
#include "gp8psk-fe.h"
u8 *buf;
if ((ret = request_firmware(&fw, bcm4500_firmware,
&d->udev->dev)) != 0) {
- err("did not find the bcm4500 firmware file. (%s) Please see linux/Documentation/dvb/ for more details on firmware-problems. (%d)",
+ err("did not find the bcm4500 firmware file '%s' (status %d). You can use <kernel_dir>/scripts/get_dvb_firmware to get the firmware",
bcm4500_firmware,ret);
return ret;
}
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 2.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#ifndef _DVB_USB_GP8PSK_H_
#define _DVB_USB_GP8PSK_H_
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, version 2.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#include "m920x.h"
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 2.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#include "dibusb.h"
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 2.
*
-* see Documentation/dvb/README.dvb-usb for more information
+* see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#define DVB_USB_LOG_PREFIX "opera"
info("start downloading fpga firmware %s",filename);
if ((ret = request_firmware(&fw, filename, &dev->dev)) != 0) {
- err("did not find the firmware file. (%s) Please see linux/Documentation/dvb/ for more details on firmware-problems.",
+ err("did not find the firmware file '%s'. You can use <kernel_dir>/scripts/get_dvb_firmware to get the firmware",
filename);
return ret;
} else {
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 2.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#define DVB_USB_LOG_PREFIX "ttusb2"
#include "dvb-usb.h"
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 2.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#ifndef _DVB_USB_TTUSB2_H_
#define _DVB_USB_TTUSB2_H_
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 2.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#include "dibusb.h"
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 2.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*
*/
#include "vp702x.h"
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 2.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#include "vp702x.h"
#include <linux/mutex.h>
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 2.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*
*/
#include "vp7045.h"
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 2.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#include "vp7045.h"
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 2.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#ifndef _DVB_USB_VP7045_H_
#define _DVB_USB_VP7045_H_
Say Y here if you want support for cameras based on the
ALi m5602 connected to various image sensors.
- See <file:Documentation/video4linux/m5602.txt> for more info.
-
To compile this driver as a module, choose M here: the
module will be called gspca_m5602.
an external software decoder to watch TV on your computer.
This driver needs external firmware. Please use the commands
- "<kerneldir>/Documentation/dvb/get_dvb_firmware dec2000t",
- "<kerneldir>/Documentation/dvb/get_dvb_firmware dec2540t",
- "<kerneldir>/Documentation/dvb/get_dvb_firmware dec3000s",
+ "<kerneldir>/scripts/get_dvb_firmware dec2000t",
+ "<kerneldir>/scripts/get_dvb_firmware dec2540t",
+ "<kerneldir>/scripts/get_dvb_firmware dec3000s",
download/extract them, and then copy them to /usr/lib/hotplug/firmware
or /lib/firmware (depending on configuration of firmware hotplug).
---help---
Say Y here if you want to connect this type of camera to your
computer's USB port.
- See <file:Documentation/video4linux/zr364xx.txt> for more info
+ See <file:Documentation/media/v4l-drivers/zr364xx.rst> for more info
and list of supported cameras.
To compile this driver as a module, choose M here: the
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/pci.h>
-#include <linux/i2c-gpio.h>
+#include <linux/platform_data/i2c-gpio.h>
#include <linux/gpio/machine.h>
#include <linux/slab.h>
#include <linux/timb_gpio.h>
#include <linux/i2c.h>
-#include <linux/i2c-ocores.h>
-#include <linux/i2c-xiic.h>
+#include <linux/platform_data/i2c-ocores.h>
+#include <linux/platform_data/i2c-xiic.h>
#include <linux/spi/spi.h>
#include <linux/spi/xilinx_spi.h>
return 0;
}
+static void at24_remove_dummy_clients(struct at24_data *at24)
+{
+ int i;
+
+ for (i = 1; i < at24->num_addresses; i++)
+ i2c_unregister_device(at24->client[i].client);
+}
+
+static int at24_make_dummy_client(struct at24_data *at24, unsigned int index,
+ struct regmap_config *regmap_config)
+{
+ struct i2c_client *base_client, *dummy_client;
+ unsigned short int addr;
+ struct regmap *regmap;
+ struct device *dev;
+
+ base_client = at24->client[0].client;
+ dev = &base_client->dev;
+ addr = base_client->addr + index;
+
+ dummy_client = i2c_new_dummy(base_client->adapter,
+ base_client->addr + index);
+ if (!dummy_client) {
+ dev_err(dev, "address 0x%02x unavailable\n", addr);
+ return -EADDRINUSE;
+ }
+
+ regmap = devm_regmap_init_i2c(dummy_client, regmap_config);
+ if (IS_ERR(regmap)) {
+ i2c_unregister_device(dummy_client);
+ return PTR_ERR(regmap);
+ }
+
+ at24->client[index].client = dummy_client;
+ at24->client[index].regmap = regmap;
+
+ return 0;
+}
+
static unsigned int at24_get_offset_adj(u8 flags, unsigned int byte_len)
{
if (flags & AT24_FLAG_MAC) {
/* use dummy devices for multiple-address chips */
for (i = 1; i < num_addresses; i++) {
- at24->client[i].client = i2c_new_dummy(client->adapter,
- client->addr + i);
- if (!at24->client[i].client) {
- dev_err(dev, "address 0x%02x unavailable\n",
- client->addr + i);
- err = -EADDRINUSE;
- goto err_clients;
- }
- at24->client[i].regmap = devm_regmap_init_i2c(
- at24->client[i].client,
- ®map_config);
- if (IS_ERR(at24->client[i].regmap)) {
- err = PTR_ERR(at24->client[i].regmap);
- goto err_clients;
+ err = at24_make_dummy_client(at24, i, ®map_config);
+ if (err) {
+ at24_remove_dummy_clients(at24);
+ return err;
}
}
nvmem_config.word_size = 1;
nvmem_config.size = pdata.byte_len;
- at24->nvmem = nvmem_register(&nvmem_config);
+ at24->nvmem = devm_nvmem_register(dev, &nvmem_config);
if (IS_ERR(at24->nvmem)) {
err = PTR_ERR(at24->nvmem);
goto err_clients;
return 0;
err_clients:
- for (i = 1; i < num_addresses; i++)
- if (at24->client[i].client)
- i2c_unregister_device(at24->client[i].client);
-
+ at24_remove_dummy_clients(at24);
pm_runtime_disable(dev);
return err;
static int at24_remove(struct i2c_client *client)
{
struct at24_data *at24;
- int i;
at24 = i2c_get_clientdata(client);
- nvmem_unregister(at24->nvmem);
-
- for (i = 1; i < at24->num_addresses; i++)
- i2c_unregister_device(at24->client[i].client);
-
+ at24_remove_dummy_clients(at24);
pm_runtime_disable(&client->dev);
pm_runtime_set_suspended(&client->dev);
struct tasklet_struct qs_err_task;
struct work_struct reset_task;
struct nicvf_work rx_mode_work;
+ /* spinlock to protect workqueue arguments from concurrent access */
+ spinlock_t rx_mode_wq_lock;
/* PTP timestamp */
struct cavium_ptp *ptp_clock;
}
}
-static void nicvf_set_rx_mode_task(struct work_struct *work_arg)
+static void __nicvf_set_rx_mode_task(u8 mode, struct xcast_addr_list *mc_addrs,
+ struct nicvf *nic)
{
- struct nicvf_work *vf_work = container_of(work_arg, struct nicvf_work,
- work.work);
- struct nicvf *nic = container_of(vf_work, struct nicvf, rx_mode_work);
union nic_mbx mbx = {};
int idx;
- if (!vf_work)
- return;
-
/* From the inside of VM code flow we have only 128 bits memory
* available to send message to host's PF, so send all mc addrs
* one by one, starting from flush command in case if kernel
mbx.xcast.msg = NIC_MBOX_MSG_RESET_XCAST;
nicvf_send_msg_to_pf(nic, &mbx);
- if (vf_work->mode & BGX_XCAST_MCAST_FILTER) {
+ if (mode & BGX_XCAST_MCAST_FILTER) {
/* once enabling filtering, we need to signal to PF to add
* its' own LMAC to the filter to accept packets for it.
*/
}
/* check if we have any specific MACs to be added to PF DMAC filter */
- if (vf_work->mc) {
+ if (mc_addrs) {
/* now go through kernel list of MACs and add them one by one */
- for (idx = 0; idx < vf_work->mc->count; idx++) {
+ for (idx = 0; idx < mc_addrs->count; idx++) {
mbx.xcast.msg = NIC_MBOX_MSG_ADD_MCAST;
- mbx.xcast.data.mac = vf_work->mc->mc[idx];
+ mbx.xcast.data.mac = mc_addrs->mc[idx];
nicvf_send_msg_to_pf(nic, &mbx);
}
- kfree(vf_work->mc);
+ kfree(mc_addrs);
}
/* and finally set rx mode for PF accordingly */
mbx.xcast.msg = NIC_MBOX_MSG_SET_XCAST;
- mbx.xcast.data.mode = vf_work->mode;
+ mbx.xcast.data.mode = mode;
nicvf_send_msg_to_pf(nic, &mbx);
}
+static void nicvf_set_rx_mode_task(struct work_struct *work_arg)
+{
+ struct nicvf_work *vf_work = container_of(work_arg, struct nicvf_work,
+ work.work);
+ struct nicvf *nic = container_of(vf_work, struct nicvf, rx_mode_work);
+ u8 mode;
+ struct xcast_addr_list *mc;
+
+ if (!vf_work)
+ return;
+
+ /* Save message data locally to prevent them from
+ * being overwritten by next ndo_set_rx_mode call().
+ */
+ spin_lock(&nic->rx_mode_wq_lock);
+ mode = vf_work->mode;
+ mc = vf_work->mc;
+ vf_work->mc = NULL;
+ spin_unlock(&nic->rx_mode_wq_lock);
+
+ __nicvf_set_rx_mode_task(mode, mc, nic);
+}
+
static void nicvf_set_rx_mode(struct net_device *netdev)
{
struct nicvf *nic = netdev_priv(netdev);
}
}
}
+ spin_lock(&nic->rx_mode_wq_lock);
+ kfree(nic->rx_mode_work.mc);
nic->rx_mode_work.mc = mc_list;
nic->rx_mode_work.mode = mode;
- queue_delayed_work(nicvf_rx_mode_wq, &nic->rx_mode_work.work, 2 * HZ);
+ queue_delayed_work(nicvf_rx_mode_wq, &nic->rx_mode_work.work, 0);
+ spin_unlock(&nic->rx_mode_wq_lock);
}
static const struct net_device_ops nicvf_netdev_ops = {
INIT_WORK(&nic->reset_task, nicvf_reset_task);
INIT_DELAYED_WORK(&nic->rx_mode_work.work, nicvf_set_rx_mode_task);
+ spin_lock_init(&nic->rx_mode_wq_lock);
err = register_netdev(netdev);
if (err) {
err = sysfs_create_group(&adapter->port[0]->dev.kobj,
&cxgb3_attr_group);
+ if (err) {
+ dev_err(&pdev->dev, "cannot create sysfs group\n");
+ goto out_close_led;
+ }
print_port_info(adapter, ai);
return 0;
+out_close_led:
+ t3_set_reg_field(adapter, A_T3DBG_GPIO_EN, F_GPIO0_OUT_VAL, 0);
+
out_free_dev:
iounmap(adapter->regs);
for (i = ai->nports0 + ai->nports1 - 1; i >= 0; --i)
to identify the adapter.
More specific information on configuring the driver is in
- <file:Documentation/networking/e100.txt>.
+ <file:Documentation/networking/e100.rst>.
To compile this driver as a module, choose M here. The module
will be called e100.
<http://support.intel.com>
More specific information on configuring the driver is in
- <file:Documentation/networking/e1000.txt>.
+ <file:Documentation/networking/e1000.rst>.
To compile this driver as a module, choose M here. The module
will be called e1000.
<http://support.intel.com>
More specific information on configuring the driver is in
- <file:Documentation/networking/e1000.txt>.
+ <file:Documentation/networking/e1000.rst>.
To compile this driver as a module, choose M here. The module
will be called igb.
<http://support.intel.com>
More specific information on configuring the driver is in
- <file:Documentation/networking/e1000.txt>.
+ <file:Documentation/networking/e1000.rst>.
To compile this driver as a module, choose M here. The module
will be called igbvf.
#define IXGBE_RSS_KEY_SIZE 40 /* size of RSS Hash Key in bytes */
u32 *rss_key;
-#ifdef CONFIG_XFRM
+#ifdef CONFIG_XFRM_OFFLOAD
struct ixgbe_ipsec *ipsec;
-#endif /* CONFIG_XFRM */
+#endif /* CONFIG_XFRM_OFFLOAD */
};
static inline u8 ixgbe_max_rss_indices(struct ixgbe_adapter *adapter)
reg |= IXGBE_SECRXCTRL_RX_DIS;
IXGBE_WRITE_REG(hw, IXGBE_SECRXCTRL, reg);
- IXGBE_WRITE_FLUSH(hw);
+ /* If both Tx and Rx are ready there are no packets
+ * that we need to flush so the loopback configuration
+ * below is not necessary.
+ */
+ t_rdy = IXGBE_READ_REG(hw, IXGBE_SECTXSTAT) &
+ IXGBE_SECTXSTAT_SECTX_RDY;
+ r_rdy = IXGBE_READ_REG(hw, IXGBE_SECRXSTAT) &
+ IXGBE_SECRXSTAT_SECRX_RDY;
+ if (t_rdy && r_rdy)
+ return;
/* If the tx fifo doesn't have link, but still has data,
* we can't clear the tx sec block. Set the MAC loopback
IXGBE_SECTXSTAT_SECTX_RDY;
r_rdy = IXGBE_READ_REG(hw, IXGBE_SECRXSTAT) &
IXGBE_SECRXSTAT_SECRX_RDY;
- } while (!t_rdy && !r_rdy && limit--);
+ } while (!(t_rdy && r_rdy) && limit--);
/* undo loopback if we played with it earlier */
if (!link) {
**/
void ixgbe_init_ipsec_offload(struct ixgbe_adapter *adapter)
{
+ struct ixgbe_hw *hw = &adapter->hw;
struct ixgbe_ipsec *ipsec;
+ u32 t_dis, r_dis;
size_t size;
- if (adapter->hw.mac.type == ixgbe_mac_82598EB)
+ if (hw->mac.type == ixgbe_mac_82598EB)
+ return;
+
+ /* If there is no support for either Tx or Rx offload
+ * we should not be advertising support for IPsec.
+ */
+ t_dis = IXGBE_READ_REG(hw, IXGBE_SECTXSTAT) &
+ IXGBE_SECTXSTAT_SECTX_OFF_DIS;
+ r_dis = IXGBE_READ_REG(hw, IXGBE_SECRXSTAT) &
+ IXGBE_SECRXSTAT_SECRX_OFF_DIS;
+ if (t_dis || r_dis)
return;
ipsec = kzalloc(sizeof(*ipsec), GFP_KERNEL);
adapter->netdev->xfrmdev_ops = &ixgbe_xfrmdev_ops;
-#define IXGBE_ESP_FEATURES (NETIF_F_HW_ESP | \
- NETIF_F_HW_ESP_TX_CSUM | \
- NETIF_F_GSO_ESP)
-
- adapter->netdev->features |= IXGBE_ESP_FEATURES;
- adapter->netdev->hw_enc_features |= IXGBE_ESP_FEATURES;
-
return;
err2:
}
#endif
+ /* To support macvlan offload we have to use num_tc to
+ * restrict the queues that can be used by the device.
+ * By doing this we can avoid reporting a false number of
+ * queues.
+ */
+ if (vmdq_i > 1)
+ netdev_set_num_tc(adapter->netdev, 1);
+
/* populate TC0 for use by pool 0 */
netdev_set_tc_queue(adapter->netdev, 0,
adapter->num_rx_queues_per_pool, 0);
#ifdef CONFIG_IXGBE_DCB
ixgbe_init_dcb(adapter);
#endif
+ ixgbe_init_ipsec_offload(adapter);
/* default flow control settings */
hw->fc.requested_mode = ixgbe_fc_full;
} else {
netdev_reset_tc(dev);
- /* To support macvlan offload we have to use num_tc to
- * restrict the queues that can be used by the device.
- * By doing this we can avoid reporting a false number of
- * queues.
- */
- if (!tc && adapter->num_rx_pools > 1)
- netdev_set_num_tc(dev, 1);
-
if (adapter->hw.mac.type == ixgbe_mac_82598EB)
adapter->hw.fc.requested_mode = adapter->last_lfc_mode;
* the TSO, so it's the exception.
*/
if (skb->encapsulation && !(features & NETIF_F_TSO_MANGLEID)) {
-#ifdef CONFIG_XFRM
+#ifdef CONFIG_XFRM_OFFLOAD
if (!skb->sp)
#endif
features &= ~NETIF_F_TSO;
if (hw->mac.type >= ixgbe_mac_82599EB)
netdev->features |= NETIF_F_SCTP_CRC;
+#ifdef CONFIG_XFRM_OFFLOAD
+#define IXGBE_ESP_FEATURES (NETIF_F_HW_ESP | \
+ NETIF_F_HW_ESP_TX_CSUM | \
+ NETIF_F_GSO_ESP)
+
+ if (adapter->ipsec)
+ netdev->features |= IXGBE_ESP_FEATURES;
+#endif
/* copy netdev features into list of user selectable features */
netdev->hw_features |= netdev->features |
NETIF_F_HW_VLAN_CTAG_FILTER |
NETIF_F_FCOE_MTU;
}
#endif /* IXGBE_FCOE */
- ixgbe_init_ipsec_offload(adapter);
-
if (adapter->flags2 & IXGBE_FLAG2_RSC_CAPABLE)
netdev->hw_features |= NETIF_F_LRO;
if (adapter->flags2 & IXGBE_FLAG2_RSC_ENABLED)
#define IXGBE_SECTXCTRL_STORE_FORWARD 0x00000004
#define IXGBE_SECTXSTAT_SECTX_RDY 0x00000001
-#define IXGBE_SECTXSTAT_ECC_TXERR 0x00000002
+#define IXGBE_SECTXSTAT_SECTX_OFF_DIS 0x00000002
+#define IXGBE_SECTXSTAT_ECC_TXERR 0x00000004
#define IXGBE_SECRXCTRL_SECRX_DIS 0x00000001
#define IXGBE_SECRXCTRL_RX_DIS 0x00000002
#define IXGBE_SECRXSTAT_SECRX_RDY 0x00000001
-#define IXGBE_SECRXSTAT_ECC_RXERR 0x00000002
+#define IXGBE_SECRXSTAT_SECRX_OFF_DIS 0x00000002
+#define IXGBE_SECRXSTAT_ECC_RXERR 0x00000004
/* LinkSec (MacSec) Registers */
#define IXGBE_LSECTXCAP 0x08A00
kfree(mlxsw_sp_rt6);
}
-static bool mlxsw_sp_fib6_rt_can_mp(const struct fib6_info *rt)
-{
- /* RTF_CACHE routes are ignored */
- return (rt->fib6_flags & (RTF_GATEWAY | RTF_ADDRCONF)) == RTF_GATEWAY;
-}
-
static struct fib6_info *
mlxsw_sp_fib6_entry_rt(const struct mlxsw_sp_fib6_entry *fib6_entry)
{
static struct mlxsw_sp_fib6_entry *
mlxsw_sp_fib6_node_mp_entry_find(const struct mlxsw_sp_fib_node *fib_node,
- const struct fib6_info *nrt, bool replace)
+ const struct fib6_info *nrt, bool append)
{
struct mlxsw_sp_fib6_entry *fib6_entry;
- if (!mlxsw_sp_fib6_rt_can_mp(nrt) || replace)
+ if (!append)
return NULL;
list_for_each_entry(fib6_entry, &fib_node->entry_list, common.list) {
break;
if (rt->fib6_metric < nrt->fib6_metric)
continue;
- if (rt->fib6_metric == nrt->fib6_metric &&
- mlxsw_sp_fib6_rt_can_mp(rt))
+ if (rt->fib6_metric == nrt->fib6_metric)
return fib6_entry;
if (rt->fib6_metric > nrt->fib6_metric)
break;
mlxsw_sp_fib6_node_entry_find(const struct mlxsw_sp_fib_node *fib_node,
const struct fib6_info *nrt, bool replace)
{
- struct mlxsw_sp_fib6_entry *fib6_entry, *fallback = NULL;
+ struct mlxsw_sp_fib6_entry *fib6_entry;
list_for_each_entry(fib6_entry, &fib_node->entry_list, common.list) {
struct fib6_info *rt = mlxsw_sp_fib6_entry_rt(fib6_entry);
continue;
if (rt->fib6_table->tb6_id != nrt->fib6_table->tb6_id)
break;
- if (replace && rt->fib6_metric == nrt->fib6_metric) {
- if (mlxsw_sp_fib6_rt_can_mp(rt) ==
- mlxsw_sp_fib6_rt_can_mp(nrt))
- return fib6_entry;
- if (mlxsw_sp_fib6_rt_can_mp(nrt))
- fallback = fallback ?: fib6_entry;
- }
+ if (replace && rt->fib6_metric == nrt->fib6_metric)
+ return fib6_entry;
if (rt->fib6_metric > nrt->fib6_metric)
- return fallback ?: fib6_entry;
+ return fib6_entry;
}
- return fallback;
+ return NULL;
}
static int
}
static int mlxsw_sp_router_fib6_add(struct mlxsw_sp *mlxsw_sp,
- struct fib6_info *rt, bool replace)
+ struct fib6_info *rt, bool replace,
+ bool append)
{
struct mlxsw_sp_fib6_entry *fib6_entry;
struct mlxsw_sp_fib_node *fib_node;
/* Before creating a new entry, try to append route to an existing
* multipath entry.
*/
- fib6_entry = mlxsw_sp_fib6_node_mp_entry_find(fib_node, rt, replace);
+ fib6_entry = mlxsw_sp_fib6_node_mp_entry_find(fib_node, rt, append);
if (fib6_entry) {
err = mlxsw_sp_fib6_entry_nexthop_add(mlxsw_sp, fib6_entry, rt);
if (err)
return 0;
}
+ /* We received an append event, yet did not find any route to
+ * append to.
+ */
+ if (WARN_ON(append)) {
+ err = -EINVAL;
+ goto err_fib6_entry_append;
+ }
+
fib6_entry = mlxsw_sp_fib6_entry_create(mlxsw_sp, fib_node, rt);
if (IS_ERR(fib6_entry)) {
err = PTR_ERR(fib6_entry);
err_fib6_node_entry_link:
mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry);
err_fib6_entry_create:
+err_fib6_entry_append:
err_fib6_entry_nexthop_add:
mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
return err;
struct mlxsw_sp_fib_event_work *fib_work =
container_of(work, struct mlxsw_sp_fib_event_work, work);
struct mlxsw_sp *mlxsw_sp = fib_work->mlxsw_sp;
- bool replace;
+ bool replace, append;
int err;
rtnl_lock();
case FIB_EVENT_ENTRY_APPEND: /* fall through */
case FIB_EVENT_ENTRY_ADD:
replace = fib_work->event == FIB_EVENT_ENTRY_REPLACE;
+ append = fib_work->event == FIB_EVENT_ENTRY_APPEND;
err = mlxsw_sp_router_fib6_add(mlxsw_sp,
- fib_work->fen6_info.rt, replace);
+ fib_work->fen6_info.rt, replace,
+ append);
if (err)
mlxsw_sp_router_fib_abort(mlxsw_sp);
mlxsw_sp_rt6_release(fib_work->fen6_info.rt);
int err;
/* No need to continue if only VLAN flags were changed */
- if (mlxsw_sp_port_vlan->bridge_port)
+ if (mlxsw_sp_port_vlan->bridge_port) {
+ mlxsw_sp_port_vlan_put(mlxsw_sp_port_vlan);
return 0;
+ }
err = mlxsw_sp_port_vlan_fid_join(mlxsw_sp_port_vlan, bridge_port);
if (err)
eth_hw_addr_random(nn->dp.netdev);
netif_keep_dst(nn->dp.netdev);
+ nn->vnic_no_name = true;
return 0;
err = PTR_ERR_OR_ZERO(rt);
if (err)
return NOTIFY_DONE;
+
+ ip_rt_put(rt);
#else
return NOTIFY_DONE;
#endif
* @vnic_list: Entry on device vNIC list
* @pdev: Backpointer to PCI device
* @app: APP handle if available
+ * @vnic_no_name: For non-port PF vNIC make ndo_get_phys_port_name return
+ * -EOPNOTSUPP to keep backwards compatibility (set by app)
* @port: Pointer to nfp_port structure if vNIC is a port
* @app_priv: APP private data for this vNIC
*/
struct pci_dev *pdev;
struct nfp_app *app;
+ bool vnic_no_name;
+
struct nfp_port *port;
void *app_priv;
struct nfp_net *nn = netdev_priv(netdev);
int r;
- for (r = 0; r < nn->dp.num_r_vecs; r++) {
+ for (r = 0; r < nn->max_r_vecs; r++) {
struct nfp_net_r_vector *r_vec = &nn->r_vecs[r];
u64 data[3];
unsigned int start;
if (nn->port)
return nfp_port_get_phys_port_name(netdev, name, len);
- if (nn->dp.is_vf)
+ if (nn->dp.is_vf || nn->vnic_no_name)
return -EOPNOTSUPP;
n = snprintf(name, len, "n%d", nn->id);
static int nfp_cpp_resource_find(struct nfp_cpp *cpp, struct nfp_resource *res)
{
- char name_pad[NFP_RESOURCE_ENTRY_NAME_SZ] = {};
struct nfp_resource_entry entry;
u32 cpp_id, key;
int ret, i;
cpp_id = NFP_CPP_ID(NFP_RESOURCE_TBL_TARGET, 3, 0); /* Atomic read */
- strncpy(name_pad, res->name, sizeof(name_pad));
-
/* Search for a matching entry */
- if (!memcmp(name_pad, NFP_RESOURCE_TBL_NAME "\0\0\0\0\0\0\0\0", 8)) {
+ if (!strcmp(res->name, NFP_RESOURCE_TBL_NAME)) {
nfp_err(cpp, "Grabbing device lock not supported\n");
return -EOPNOTSUPP;
}
- key = crc32_posix(name_pad, sizeof(name_pad));
+ key = crc32_posix(res->name, NFP_RESOURCE_ENTRY_NAME_SZ);
for (i = 0; i < NFP_RESOURCE_TBL_ENTRIES; i++) {
u64 addr = NFP_RESOURCE_TBL_BASE +
}
sgmii_pdev = of_find_device_by_node(np);
+ of_node_put(np);
if (!sgmii_pdev) {
dev_err(&pdev->dev, "invalid internal-phy property\n");
return -ENODEV;
dwmac->data = (const struct meson8b_dwmac_data *)
of_device_get_match_data(&pdev->dev);
- if (!dwmac->data)
- return -EINVAL;
-
+ if (!dwmac->data) {
+ ret = -EINVAL;
+ goto err_remove_config_dt;
+ }
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
dwmac->regs = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(dwmac->regs)) {
return ret;
}
- /* Run quirks, if needed */
- if (entry->quirks) {
- ret = entry->quirks(priv);
- if (ret)
- return ret;
- }
-
+ /* Save quirks, if needed for posterior use */
+ priv->hwif_quirks = entry->quirks;
return 0;
}
struct net_device *dev;
struct device *device;
struct mac_device_info *hw;
+ int (*hwif_quirks)(struct stmmac_priv *priv);
struct mutex lock;
/* RX Queue */
static void stmmac_rx_vlan(struct net_device *dev, struct sk_buff *skb)
{
- struct ethhdr *ehdr;
+ struct vlan_ethhdr *veth;
+ __be16 vlan_proto;
u16 vlanid;
- if ((dev->features & NETIF_F_HW_VLAN_CTAG_RX) ==
- NETIF_F_HW_VLAN_CTAG_RX &&
- !__vlan_get_tag(skb, &vlanid)) {
+ veth = (struct vlan_ethhdr *)skb->data;
+ vlan_proto = veth->h_vlan_proto;
+
+ if ((vlan_proto == htons(ETH_P_8021Q) &&
+ dev->features & NETIF_F_HW_VLAN_CTAG_RX) ||
+ (vlan_proto == htons(ETH_P_8021AD) &&
+ dev->features & NETIF_F_HW_VLAN_STAG_RX)) {
/* pop the vlan tag */
- ehdr = (struct ethhdr *)skb->data;
- memmove(skb->data + VLAN_HLEN, ehdr, ETH_ALEN * 2);
+ vlanid = ntohs(veth->h_vlan_TCI);
+ memmove(skb->data + VLAN_HLEN, veth, ETH_ALEN * 2);
skb_pull(skb, VLAN_HLEN);
- __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vlanid);
+ __vlan_hwaccel_put_tag(skb, vlan_proto, vlanid);
}
}
if (priv->dma_cap.tsoen)
dev_info(priv->device, "TSO supported\n");
+ /* Run HW quirks, if any */
+ if (priv->hwif_quirks) {
+ ret = priv->hwif_quirks(priv);
+ if (ret)
+ return ret;
+ }
+
return 0;
}
ndev->watchdog_timeo = msecs_to_jiffies(watchdog);
#ifdef STMMAC_VLAN_TAG_USED
/* Both mac100 and gmac support receive VLAN tag detection */
- ndev->features |= NETIF_F_HW_VLAN_CTAG_RX;
+ ndev->features |= NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_STAG_RX;
#endif
priv->msg_enable = netif_msg_init(debug, default_msg_level);
* @phy_node: pointer to the PHY device node
* @mii_bus: pointer to the MII bus
* @last_link: last link status
- * @has_mdio: indicates whether MDIO is included in the HW
*/
struct net_local {
struct mii_bus *mii_bus;
int last_link;
- bool has_mdio;
};
bus->write = xemaclite_mdio_write;
bus->parent = dev;
- lp->mii_bus = bus;
-
rc = of_mdiobus_register(bus, np);
if (rc) {
dev_err(dev, "Failed to register mdio bus.\n");
goto err_register;
}
+ lp->mii_bus = bus;
+
return 0;
err_register:
xemaclite_update_address(lp, ndev->dev_addr);
lp->phy_node = of_parse_phandle(ofdev->dev.of_node, "phy-handle", 0);
- rc = xemaclite_mdio_setup(lp, &ofdev->dev);
- if (rc)
- dev_warn(&ofdev->dev, "error registering MDIO bus\n");
+ xemaclite_mdio_setup(lp, &ofdev->dev);
dev_info(dev, "MAC address is now %pM\n", ndev->dev_addr);
struct net_local *lp = netdev_priv(ndev);
/* Un-register the mii_bus, if configured */
- if (lp->has_mdio) {
+ if (lp->mii_bus) {
mdiobus_unregister(lp->mii_bus);
mdiobus_free(lp->mii_bus);
lp->mii_bus = NULL;
tristate "Microsoft Hyper-V virtual network driver"
depends on HYPERV
select UCS2_STRING
- select FAILOVER
help
Select this option to enable the Hyper-V virtual network driver.
struct hv_device *device_ctx;
/* netvsc_device */
struct netvsc_device __rcu *nvdev;
+ /* list of netvsc net_devices */
+ struct list_head list;
/* reconfigure work */
struct delayed_work dwork;
/* last reconfig time */
u32 vf_alloc;
/* Serial number of the VF to team with */
u32 vf_serial;
-
- struct failover *failover;
};
/* Per channel data */
struct ndis_ipsecv2_offload {
u32 encap;
- u16 ip6;
- u16 ip4opt;
- u16 ip6ext;
- u16 ah;
- u16 esp;
- u16 ah_esp;
- u16 xport;
- u16 tun;
- u16 xport_tun;
- u16 lso;
- u16 extseq;
+ u8 ip6;
+ u8 ip4opt;
+ u8 ip6ext;
+ u8 ah;
+ u8 esp;
+ u8 ah_esp;
+ u8 xport;
+ u8 tun;
+ u8 xport_tun;
+ u8 lso;
+ u8 extseq;
u32 udp_esp;
u32 auth;
u32 crypto;
};
struct ndis_rsc_offload {
- u16 ip4;
- u16 ip6;
+ u8 ip4;
+ u8 ip6;
};
struct ndis_encap_offload {
#include <net/pkt_sched.h>
#include <net/checksum.h>
#include <net/ip6_checksum.h>
-#include <net/failover.h>
#include "hyperv_net.h"
module_param(debug, int, 0444);
MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)");
+static LIST_HEAD(netvsc_dev_list);
+
static void netvsc_change_rx_flags(struct net_device *net, int change)
{
struct net_device_context *ndev_ctx = netdev_priv(net);
rtnl_unlock();
}
+static struct net_device *get_netvsc_bymac(const u8 *mac)
+{
+ struct net_device_context *ndev_ctx;
+
+ list_for_each_entry(ndev_ctx, &netvsc_dev_list, list) {
+ struct net_device *dev = hv_get_drvdata(ndev_ctx->device_ctx);
+
+ if (ether_addr_equal(mac, dev->perm_addr))
+ return dev;
+ }
+
+ return NULL;
+}
+
+static struct net_device *get_netvsc_byref(struct net_device *vf_netdev)
+{
+ struct net_device_context *net_device_ctx;
+ struct net_device *dev;
+
+ dev = netdev_master_upper_dev_get(vf_netdev);
+ if (!dev || dev->netdev_ops != &device_ops)
+ return NULL; /* not a netvsc device */
+
+ net_device_ctx = netdev_priv(dev);
+ if (!rtnl_dereference(net_device_ctx->nvdev))
+ return NULL; /* device is removed */
+
+ return dev;
+}
+
/* Called when VF is injecting data into network stack.
* Change the associated network device from VF to netvsc.
* note: already called with rcu_read_lock
return RX_HANDLER_ANOTHER;
}
+static int netvsc_vf_join(struct net_device *vf_netdev,
+ struct net_device *ndev)
+{
+ struct net_device_context *ndev_ctx = netdev_priv(ndev);
+ int ret;
+
+ ret = netdev_rx_handler_register(vf_netdev,
+ netvsc_vf_handle_frame, ndev);
+ if (ret != 0) {
+ netdev_err(vf_netdev,
+ "can not register netvsc VF receive handler (err = %d)\n",
+ ret);
+ goto rx_handler_failed;
+ }
+
+ ret = netdev_master_upper_dev_link(vf_netdev, ndev,
+ NULL, NULL, NULL);
+ if (ret != 0) {
+ netdev_err(vf_netdev,
+ "can not set master device %s (err = %d)\n",
+ ndev->name, ret);
+ goto upper_link_failed;
+ }
+
+ /* set slave flag before open to prevent IPv6 addrconf */
+ vf_netdev->flags |= IFF_SLAVE;
+
+ schedule_delayed_work(&ndev_ctx->vf_takeover, VF_TAKEOVER_INT);
+
+ call_netdevice_notifiers(NETDEV_JOIN, vf_netdev);
+
+ netdev_info(vf_netdev, "joined to %s\n", ndev->name);
+ return 0;
+
+upper_link_failed:
+ netdev_rx_handler_unregister(vf_netdev);
+rx_handler_failed:
+ return ret;
+}
+
static void __netvsc_vf_setup(struct net_device *ndev,
struct net_device *vf_netdev)
{
rtnl_unlock();
}
-static int netvsc_pre_register_vf(struct net_device *vf_netdev,
- struct net_device *ndev)
+static int netvsc_register_vf(struct net_device *vf_netdev)
{
+ struct net_device *ndev;
struct net_device_context *net_device_ctx;
struct netvsc_device *netvsc_dev;
+ int ret;
+
+ if (vf_netdev->addr_len != ETH_ALEN)
+ return NOTIFY_DONE;
+
+ /*
+ * We will use the MAC address to locate the synthetic interface to
+ * associate with the VF interface. If we don't find a matching
+ * synthetic interface, move on.
+ */
+ ndev = get_netvsc_bymac(vf_netdev->perm_addr);
+ if (!ndev)
+ return NOTIFY_DONE;
net_device_ctx = netdev_priv(ndev);
netvsc_dev = rtnl_dereference(net_device_ctx->nvdev);
if (!netvsc_dev || rtnl_dereference(net_device_ctx->vf_netdev))
- return -ENODEV;
-
- return 0;
-}
+ return NOTIFY_DONE;
-static int netvsc_register_vf(struct net_device *vf_netdev,
- struct net_device *ndev)
-{
- struct net_device_context *ndev_ctx = netdev_priv(ndev);
-
- /* set slave flag before open to prevent IPv6 addrconf */
- vf_netdev->flags |= IFF_SLAVE;
-
- schedule_delayed_work(&ndev_ctx->vf_takeover, VF_TAKEOVER_INT);
+ /* if syntihetic interface is a different namespace,
+ * then move the VF to that namespace; join will be
+ * done again in that context.
+ */
+ if (!net_eq(dev_net(ndev), dev_net(vf_netdev))) {
+ ret = dev_change_net_namespace(vf_netdev,
+ dev_net(ndev), "eth%d");
+ if (ret)
+ netdev_err(vf_netdev,
+ "could not move to same namespace as %s: %d\n",
+ ndev->name, ret);
+ else
+ netdev_info(vf_netdev,
+ "VF moved to namespace with: %s\n",
+ ndev->name);
+ return NOTIFY_DONE;
+ }
- call_netdevice_notifiers(NETDEV_JOIN, vf_netdev);
+ netdev_info(ndev, "VF registering: %s\n", vf_netdev->name);
- netdev_info(vf_netdev, "joined to %s\n", ndev->name);
+ if (netvsc_vf_join(vf_netdev, ndev) != 0)
+ return NOTIFY_DONE;
dev_hold(vf_netdev);
- rcu_assign_pointer(ndev_ctx->vf_netdev, vf_netdev);
-
- return 0;
+ rcu_assign_pointer(net_device_ctx->vf_netdev, vf_netdev);
+ return NOTIFY_OK;
}
/* VF up/down change detected, schedule to change data path */
-static int netvsc_vf_changed(struct net_device *vf_netdev,
- struct net_device *ndev)
+static int netvsc_vf_changed(struct net_device *vf_netdev)
{
struct net_device_context *net_device_ctx;
struct netvsc_device *netvsc_dev;
+ struct net_device *ndev;
bool vf_is_up = netif_running(vf_netdev);
+ ndev = get_netvsc_byref(vf_netdev);
+ if (!ndev)
+ return NOTIFY_DONE;
+
net_device_ctx = netdev_priv(ndev);
netvsc_dev = rtnl_dereference(net_device_ctx->nvdev);
if (!netvsc_dev)
- return -ENODEV;
+ return NOTIFY_DONE;
netvsc_switch_datapath(ndev, vf_is_up);
netdev_info(ndev, "Data path switched %s VF: %s\n",
vf_is_up ? "to" : "from", vf_netdev->name);
- return 0;
+ return NOTIFY_OK;
}
-static int netvsc_pre_unregister_vf(struct net_device *vf_netdev,
- struct net_device *ndev)
+static int netvsc_unregister_vf(struct net_device *vf_netdev)
{
+ struct net_device *ndev;
struct net_device_context *net_device_ctx;
- net_device_ctx = netdev_priv(ndev);
- cancel_delayed_work_sync(&net_device_ctx->vf_takeover);
-
- return 0;
-}
-
-static int netvsc_unregister_vf(struct net_device *vf_netdev,
- struct net_device *ndev)
-{
- struct net_device_context *net_device_ctx;
+ ndev = get_netvsc_byref(vf_netdev);
+ if (!ndev)
+ return NOTIFY_DONE;
net_device_ctx = netdev_priv(ndev);
+ cancel_delayed_work_sync(&net_device_ctx->vf_takeover);
netdev_info(ndev, "VF unregistering: %s\n", vf_netdev->name);
+ netdev_rx_handler_unregister(vf_netdev);
+ netdev_upper_dev_unlink(vf_netdev, ndev);
RCU_INIT_POINTER(net_device_ctx->vf_netdev, NULL);
dev_put(vf_netdev);
- return 0;
+ return NOTIFY_OK;
}
-static struct failover_ops netvsc_failover_ops = {
- .slave_pre_register = netvsc_pre_register_vf,
- .slave_register = netvsc_register_vf,
- .slave_pre_unregister = netvsc_pre_unregister_vf,
- .slave_unregister = netvsc_unregister_vf,
- .slave_link_change = netvsc_vf_changed,
- .slave_handle_frame = netvsc_vf_handle_frame,
-};
-
static int netvsc_probe(struct hv_device *dev,
const struct hv_vmbus_device_id *dev_id)
{
else
net->max_mtu = ETH_DATA_LEN;
- ret = register_netdev(net);
+ rtnl_lock();
+ ret = register_netdevice(net);
if (ret != 0) {
pr_err("Unable to register netdev.\n");
goto register_failed;
}
- net_device_ctx->failover = failover_register(net, &netvsc_failover_ops);
- if (IS_ERR(net_device_ctx->failover)) {
- ret = PTR_ERR(net_device_ctx->failover);
- goto err_failover;
- }
-
- return ret;
+ list_add(&net_device_ctx->list, &netvsc_dev_list);
+ rtnl_unlock();
+ return 0;
-err_failover:
- unregister_netdev(net);
register_failed:
+ rtnl_unlock();
rndis_filter_device_remove(dev, nvdev);
rndis_failed:
free_percpu(net_device_ctx->vf_stats);
rtnl_lock();
vf_netdev = rtnl_dereference(ndev_ctx->vf_netdev);
if (vf_netdev)
- failover_slave_unregister(vf_netdev);
+ netvsc_unregister_vf(vf_netdev);
if (nvdev)
rndis_filter_device_remove(dev, nvdev);
unregister_netdevice(net);
-
- failover_unregister(ndev_ctx->failover);
+ list_del(&ndev_ctx->list);
rtnl_unlock();
rcu_read_unlock();
.remove = netvsc_remove,
};
+/*
+ * On Hyper-V, every VF interface is matched with a corresponding
+ * synthetic interface. The synthetic interface is presented first
+ * to the guest. When the corresponding VF instance is registered,
+ * we will take care of switching the data path.
+ */
+static int netvsc_netdev_event(struct notifier_block *this,
+ unsigned long event, void *ptr)
+{
+ struct net_device *event_dev = netdev_notifier_info_to_dev(ptr);
+
+ /* Skip our own events */
+ if (event_dev->netdev_ops == &device_ops)
+ return NOTIFY_DONE;
+
+ /* Avoid non-Ethernet type devices */
+ if (event_dev->type != ARPHRD_ETHER)
+ return NOTIFY_DONE;
+
+ /* Avoid Vlan dev with same MAC registering as VF */
+ if (is_vlan_dev(event_dev))
+ return NOTIFY_DONE;
+
+ /* Avoid Bonding master dev with same MAC registering as VF */
+ if ((event_dev->priv_flags & IFF_BONDING) &&
+ (event_dev->flags & IFF_MASTER))
+ return NOTIFY_DONE;
+
+ switch (event) {
+ case NETDEV_REGISTER:
+ return netvsc_register_vf(event_dev);
+ case NETDEV_UNREGISTER:
+ return netvsc_unregister_vf(event_dev);
+ case NETDEV_UP:
+ case NETDEV_DOWN:
+ return netvsc_vf_changed(event_dev);
+ default:
+ return NOTIFY_DONE;
+ }
+}
+
+static struct notifier_block netvsc_netdev_notifier = {
+ .notifier_call = netvsc_netdev_event,
+};
+
static void __exit netvsc_drv_exit(void)
{
+ unregister_netdevice_notifier(&netvsc_netdev_notifier);
vmbus_driver_unregister(&netvsc_drv);
}
if (ret)
return ret;
+ register_netdevice_notifier(&netvsc_netdev_notifier);
return 0;
}
#include <linux/platform_device.h>
#include <linux/mdio-bitbang.h>
#include <linux/mdio-gpio.h>
-#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
-
-#include <linux/of_gpio.h>
#include <linux/of_mdio.h>
struct mdio_gpio_info {
hwsim_wq = alloc_workqueue("hwsim_wq", 0, 0);
if (!hwsim_wq)
return -ENOMEM;
- rhashtable_init(&hwsim_radios_rht, &hwsim_rht_params);
+
+ err = rhashtable_init(&hwsim_radios_rht, &hwsim_rht_params);
+ if (err)
+ goto out_free_wq;
err = register_pernet_device(&hwsim_net_ops);
if (err)
- return err;
+ goto out_free_rht;
err = platform_driver_register(&mac80211_hwsim_driver);
if (err)
platform_driver_unregister(&mac80211_hwsim_driver);
out_unregister_pernet:
unregister_pernet_device(&hwsim_net_ops);
+out_free_rht:
+ rhashtable_destroy(&hwsim_radios_rht);
+out_free_wq:
+ destroy_workqueue(hwsim_wq);
return err;
}
module_init(init_mac80211_hwsim);
static int netfront_tx_slot_available(struct netfront_queue *queue)
{
return (queue->tx.req_prod_pvt - queue->tx.rsp_cons) <
- (NET_TX_RING_SIZE - MAX_SKB_FRAGS - 2);
+ (NET_TX_RING_SIZE - XEN_NETIF_NR_SLOTS_MIN - 1);
}
static void xennet_maybe_wake_tx(struct netfront_queue *queue)
RING_IDX cons = queue->rx.rsp_cons;
struct sk_buff *skb = xennet_get_rx_skb(queue, cons);
grant_ref_t ref = xennet_get_rx_ref(queue, cons);
- int max = MAX_SKB_FRAGS + (rx->status <= RX_COPY_THRESHOLD);
+ int max = XEN_NETIF_NR_SLOTS_MIN + (rx->status <= RX_COPY_THRESHOLD);
int slots = 1;
int err = 0;
unsigned long ret;
* Verify that the subsystem actually supports multiple
* controllers, else bail out.
*/
- if (!ctrl->opts->discovery_nqn &&
+ if (!(ctrl->opts && ctrl->opts->discovery_nqn) &&
nvme_active_ctrls(found) && !(id->cmic & (1 << 1))) {
dev_err(ctrl->device,
"ignoring ctrl due to duplicate subnqn (%s).\n",
nvme_remove_invalid_namespaces(ctrl, nn);
}
-static bool nvme_scan_changed_ns_log(struct nvme_ctrl *ctrl)
+static void nvme_clear_changed_ns_log(struct nvme_ctrl *ctrl)
{
size_t log_size = NVME_MAX_CHANGED_NAMESPACES * sizeof(__le32);
__le32 *log;
- int error, i;
- bool ret = false;
+ int error;
log = kzalloc(log_size, GFP_KERNEL);
if (!log)
- return false;
+ return;
+ /*
+ * We need to read the log to clear the AEN, but we don't want to rely
+ * on it for the changed namespace information as userspace could have
+ * raced with us in reading the log page, which could cause us to miss
+ * updates.
+ */
error = nvme_get_log(ctrl, NVME_LOG_CHANGED_NS, log, log_size);
- if (error) {
+ if (error)
dev_warn(ctrl->device,
"reading changed ns log failed: %d\n", error);
- goto out_free_log;
- }
-
- if (log[0] == cpu_to_le32(0xffffffff))
- goto out_free_log;
-
- for (i = 0; i < NVME_MAX_CHANGED_NAMESPACES; i++) {
- u32 nsid = le32_to_cpu(log[i]);
- if (nsid == 0)
- break;
- dev_info(ctrl->device, "rescanning namespace %d.\n", nsid);
- nvme_validate_ns(ctrl, nsid);
- }
- ret = true;
-
-out_free_log:
kfree(log);
- return ret;
}
static void nvme_scan_work(struct work_struct *work)
WARN_ON_ONCE(!ctrl->tagset);
if (test_and_clear_bit(NVME_AER_NOTICE_NS_CHANGED, &ctrl->events)) {
- if (nvme_scan_changed_ns_log(ctrl))
- goto out_sort_namespaces;
dev_info(ctrl->device, "rescanning namespaces.\n");
+ nvme_clear_changed_ns_log(ctrl);
}
if (nvme_identify_ctrl(ctrl, &id))
nvme_scan_ns_sequential(ctrl, nn);
out_free_id:
kfree(id);
-out_sort_namespaces:
down_write(&ctrl->namespaces_rwsem);
list_sort(NULL, &ctrl->namespaces, ns_cmp);
up_write(&ctrl->namespaces_rwsem);
}
EXPORT_SYMBOL_GPL(nvme_start_queues);
-int nvme_reinit_tagset(struct nvme_ctrl *ctrl, struct blk_mq_tag_set *set)
-{
- if (!ctrl->ops->reinit_request)
- return 0;
-
- return blk_mq_tagset_iter(set, set->driver_data,
- ctrl->ops->reinit_request);
-}
-EXPORT_SYMBOL_GPL(nvme_reinit_tagset);
-
int __init nvme_core_init(void)
{
int result = -ENOMEM;
return NULL;
}
-blk_status_t nvmf_check_if_ready(struct nvme_ctrl *ctrl, struct request *rq,
- bool queue_live, bool is_connected)
+/*
+ * For something we're not in a state to send to the device the default action
+ * is to busy it and retry it after the controller state is recovered. However,
+ * anything marked for failfast or nvme multipath is immediately failed.
+ *
+ * Note: commands used to initialize the controller will be marked for failfast.
+ * Note: nvme cli/ioctl commands are marked for failfast.
+ */
+blk_status_t nvmf_fail_nonready_command(struct request *rq)
{
- struct nvme_command *cmd = nvme_req(rq)->cmd;
+ if (!blk_noretry_request(rq) && !(rq->cmd_flags & REQ_NVME_MPATH))
+ return BLK_STS_RESOURCE;
+ nvme_req(rq)->status = NVME_SC_ABORT_REQ;
+ return BLK_STS_IOERR;
+}
+EXPORT_SYMBOL_GPL(nvmf_fail_nonready_command);
- if (likely(ctrl->state == NVME_CTRL_LIVE && is_connected))
- return BLK_STS_OK;
+bool __nvmf_check_ready(struct nvme_ctrl *ctrl, struct request *rq,
+ bool queue_live)
+{
+ struct nvme_request *req = nvme_req(rq);
+
+ /*
+ * If we are in some state of setup or teardown only allow
+ * internally generated commands.
+ */
+ if (!blk_rq_is_passthrough(rq) || (req->flags & NVME_REQ_USERCMD))
+ return false;
+ /*
+ * Only allow commands on a live queue, except for the connect command,
+ * which is require to set the queue live in the appropinquate states.
+ */
switch (ctrl->state) {
case NVME_CTRL_NEW:
case NVME_CTRL_CONNECTING:
- case NVME_CTRL_DELETING:
- /*
- * This is the case of starting a new or deleting an association
- * but connectivity was lost before it was fully created or torn
- * down. We need to error the commands used to initialize the
- * controller so the reconnect can go into a retry attempt. The
- * commands should all be marked REQ_FAILFAST_DRIVER, which will
- * hit the reject path below. Anything else will be queued while
- * the state settles.
- */
- if (!is_connected)
- break;
-
- /*
- * If queue is live, allow only commands that are internally
- * generated pass through. These are commands on the admin
- * queue to initialize the controller. This will reject any
- * ioctl admin cmds received while initializing.
- */
- if (queue_live && !(nvme_req(rq)->flags & NVME_REQ_USERCMD))
- return BLK_STS_OK;
-
- /*
- * If the queue is not live, allow only a connect command. This
- * will reject any ioctl admin cmd as well as initialization
- * commands if the controller reverted the queue to non-live.
- */
- if (!queue_live && blk_rq_is_passthrough(rq) &&
- cmd->common.opcode == nvme_fabrics_command &&
- cmd->fabrics.fctype == nvme_fabrics_type_connect)
- return BLK_STS_OK;
+ if (req->cmd->common.opcode == nvme_fabrics_command &&
+ req->cmd->fabrics.fctype == nvme_fabrics_type_connect)
+ return true;
break;
default:
break;
+ case NVME_CTRL_DEAD:
+ return false;
}
- /*
- * Any other new io is something we're not in a state to send to the
- * device. Default action is to busy it and retry it after the
- * controller state is recovered. However, anything marked for failfast
- * or nvme multipath is immediately failed. Note: commands used to
- * initialize the controller will be marked for failfast.
- * Note: nvme cli/ioctl commands are marked for failfast.
- */
- if (!blk_noretry_request(rq) && !(rq->cmd_flags & REQ_NVME_MPATH))
- return BLK_STS_RESOURCE;
- nvme_req(rq)->status = NVME_SC_ABORT_REQ;
- return BLK_STS_IOERR;
+ return queue_live;
}
-EXPORT_SYMBOL_GPL(nvmf_check_if_ready);
+EXPORT_SYMBOL_GPL(__nvmf_check_ready);
static const match_table_t opt_tokens = {
{ NVMF_OPT_TRANSPORT, "transport=%s" },
void nvmf_free_options(struct nvmf_ctrl_options *opts);
int nvmf_get_address(struct nvme_ctrl *ctrl, char *buf, int size);
bool nvmf_should_reconnect(struct nvme_ctrl *ctrl);
-blk_status_t nvmf_check_if_ready(struct nvme_ctrl *ctrl,
- struct request *rq, bool queue_live, bool is_connected);
+blk_status_t nvmf_fail_nonready_command(struct request *rq);
+bool __nvmf_check_ready(struct nvme_ctrl *ctrl, struct request *rq,
+ bool queue_live);
+
+static inline bool nvmf_check_ready(struct nvme_ctrl *ctrl, struct request *rq,
+ bool queue_live)
+{
+ if (likely(ctrl->state == NVME_CTRL_LIVE ||
+ ctrl->state == NVME_CTRL_ADMIN_ONLY))
+ return true;
+ return __nvmf_check_ready(ctrl, rq, queue_live);
+}
#endif /* _NVME_FABRICS_H */
struct nvme_fc_rport *rport;
u32 cnum;
+ bool ioq_live;
bool assoc_active;
u64 association_id;
static void nvme_fc_error_recovery(struct nvme_fc_ctrl *ctrl, char *errmsg);
-static int
-nvme_fc_reinit_request(void *data, struct request *rq)
-{
- struct nvme_fc_fcp_op *op = blk_mq_rq_to_pdu(rq);
- struct nvme_fc_cmd_iu *cmdiu = &op->cmd_iu;
-
- memset(cmdiu, 0, sizeof(*cmdiu));
- cmdiu->scsi_id = NVME_CMD_SCSI_ID;
- cmdiu->fc_id = NVME_CMD_FC_ID;
- cmdiu->iu_len = cpu_to_be16(sizeof(*cmdiu) / sizeof(u32));
- memset(&op->rsp_iu, 0, sizeof(op->rsp_iu));
-
- return 0;
-}
-
static void
__nvme_fc_exit_request(struct nvme_fc_ctrl *ctrl,
struct nvme_fc_fcp_op *op)
*/
queue->connection_id = 0;
+ atomic_set(&queue->csn, 1);
}
static void
struct nvme_fc_cmd_iu *cmdiu = &op->cmd_iu;
struct nvme_command *sqe = &cmdiu->sqe;
enum nvmefc_fcp_datadir io_dir;
+ bool queue_ready = test_bit(NVME_FC_Q_LIVE, &queue->flags);
u32 data_len;
blk_status_t ret;
- ret = nvmf_check_if_ready(&queue->ctrl->ctrl, rq,
- test_bit(NVME_FC_Q_LIVE, &queue->flags),
- ctrl->rport->remoteport.port_state == FC_OBJSTATE_ONLINE);
- if (unlikely(ret))
- return ret;
+ if (ctrl->rport->remoteport.port_state != FC_OBJSTATE_ONLINE ||
+ !nvmf_check_ready(&queue->ctrl->ctrl, rq, queue_ready))
+ return nvmf_fail_nonready_command(rq);
ret = nvme_setup_cmd(ns, rq, sqe);
if (ret)
if (ret)
goto out_delete_hw_queues;
+ ctrl->ioq_live = true;
+
return 0;
out_delete_hw_queues:
}
static int
-nvme_fc_reinit_io_queues(struct nvme_fc_ctrl *ctrl)
+nvme_fc_recreate_io_queues(struct nvme_fc_ctrl *ctrl)
{
struct nvmf_ctrl_options *opts = ctrl->ctrl.opts;
unsigned int nr_io_queues;
if (ctrl->ctrl.queue_count == 1)
return 0;
- nvme_fc_init_io_queues(ctrl);
-
- ret = nvme_reinit_tagset(&ctrl->ctrl, ctrl->ctrl.tagset);
- if (ret)
- goto out_free_io_queues;
-
ret = nvme_fc_create_hw_io_queues(ctrl, ctrl->ctrl.sqsize + 1);
if (ret)
goto out_free_io_queues;
* Create the admin queue
*/
- nvme_fc_init_queue(ctrl, 0);
-
ret = __nvme_fc_create_hw_queue(ctrl, &ctrl->queues[0], 0,
NVME_AQ_DEPTH);
if (ret)
if (ret)
goto out_delete_hw_queue;
- if (ctrl->ctrl.state != NVME_CTRL_NEW)
- blk_mq_unquiesce_queue(ctrl->ctrl.admin_q);
+ blk_mq_unquiesce_queue(ctrl->ctrl.admin_q);
ret = nvmf_connect_admin_queue(&ctrl->ctrl);
if (ret)
*/
if (ctrl->ctrl.queue_count > 1) {
- if (ctrl->ctrl.state == NVME_CTRL_NEW)
+ if (!ctrl->ioq_live)
ret = nvme_fc_create_io_queues(ctrl);
else
- ret = nvme_fc_reinit_io_queues(ctrl);
+ ret = nvme_fc_recreate_io_queues(ctrl);
if (ret)
goto out_term_aen_ops;
}
* use blk_mq_tagset_busy_itr() and the transport routine to
* terminate the exchanges.
*/
- if (ctrl->ctrl.state != NVME_CTRL_NEW)
- blk_mq_quiesce_queue(ctrl->ctrl.admin_q);
+ blk_mq_quiesce_queue(ctrl->ctrl.admin_q);
blk_mq_tagset_busy_iter(&ctrl->admin_tag_set,
nvme_fc_terminate_exchange, &ctrl->ctrl);
.submit_async_event = nvme_fc_submit_async_event,
.delete_ctrl = nvme_fc_delete_ctrl,
.get_address = nvmf_get_address,
- .reinit_request = nvme_fc_reinit_request,
};
static void
nvme_fc_reconnect_or_delete(ctrl, ret);
else
dev_info(ctrl->ctrl.device,
- "NVME-FC{%d}: controller reconnect complete\n",
+ "NVME-FC{%d}: controller connect complete\n",
ctrl->cnum);
}
{
struct nvme_fc_ctrl *ctrl;
unsigned long flags;
- int ret, idx, retry;
+ int ret, idx;
if (!(rport->remoteport.port_role &
(FC_PORT_ROLE_NVME_DISCOVERY | FC_PORT_ROLE_NVME_TARGET))) {
}
ctrl->ctrl.opts = opts;
+ ctrl->ctrl.nr_reconnects = 0;
INIT_LIST_HEAD(&ctrl->ctrl_list);
ctrl->lport = lport;
ctrl->rport = rport;
ctrl->dev = lport->dev;
ctrl->cnum = idx;
+ ctrl->ioq_live = false;
ctrl->assoc_active = false;
init_waitqueue_head(&ctrl->ioabort_wait);
ctrl->ctrl.sqsize = opts->queue_size - 1;
ctrl->ctrl.kato = opts->kato;
+ ctrl->ctrl.cntlid = 0xffff;
ret = -ENOMEM;
ctrl->queues = kcalloc(ctrl->ctrl.queue_count,
if (!ctrl->queues)
goto out_free_ida;
+ nvme_fc_init_queue(ctrl, 0);
+
memset(&ctrl->admin_tag_set, 0, sizeof(ctrl->admin_tag_set));
ctrl->admin_tag_set.ops = &nvme_fc_admin_mq_ops;
ctrl->admin_tag_set.queue_depth = NVME_AQ_MQ_TAG_DEPTH;
list_add_tail(&ctrl->ctrl_list, &rport->ctrl_list);
spin_unlock_irqrestore(&rport->lock, flags);
- /*
- * It's possible that transactions used to create the association
- * may fail. Examples: CreateAssociation LS or CreateIOConnection
- * LS gets dropped/corrupted/fails; or a frame gets dropped or a
- * command times out for one of the actions to init the controller
- * (Connect, Get/Set_Property, Set_Features, etc). Many of these
- * transport errors (frame drop, LS failure) inherently must kill
- * the association. The transport is coded so that any command used
- * to create the association (prior to a LIVE state transition
- * while NEW or CONNECTING) will fail if it completes in error or
- * times out.
- *
- * As such: as the connect request was mostly likely due to a
- * udev event that discovered the remote port, meaning there is
- * not an admin or script there to restart if the connect
- * request fails, retry the initial connection creation up to
- * three times before giving up and declaring failure.
- */
- for (retry = 0; retry < 3; retry++) {
- ret = nvme_fc_create_association(ctrl);
- if (!ret)
- break;
- }
-
- if (ret) {
- nvme_change_ctrl_state(&ctrl->ctrl, NVME_CTRL_DELETING);
- cancel_work_sync(&ctrl->ctrl.reset_work);
- cancel_delayed_work_sync(&ctrl->connect_work);
-
- /* couldn't schedule retry - fail out */
+ if (!nvme_change_ctrl_state(&ctrl->ctrl, NVME_CTRL_RESETTING) ||
+ !nvme_change_ctrl_state(&ctrl->ctrl, NVME_CTRL_CONNECTING)) {
dev_err(ctrl->ctrl.device,
- "NVME-FC{%d}: Connect retry failed\n", ctrl->cnum);
-
- ctrl->ctrl.opts = NULL;
+ "NVME-FC{%d}: failed to init ctrl state\n", ctrl->cnum);
+ goto fail_ctrl;
+ }
- /* initiate nvme ctrl ref counting teardown */
- nvme_uninit_ctrl(&ctrl->ctrl);
+ nvme_get_ctrl(&ctrl->ctrl);
- /* Remove core ctrl ref. */
+ if (!queue_delayed_work(nvme_wq, &ctrl->connect_work, 0)) {
nvme_put_ctrl(&ctrl->ctrl);
-
- /* as we're past the point where we transition to the ref
- * counting teardown path, if we return a bad pointer here,
- * the calling routine, thinking it's prior to the
- * transition, will do an rport put. Since the teardown
- * path also does a rport put, we do an extra get here to
- * so proper order/teardown happens.
- */
- nvme_fc_rport_get(rport);
-
- if (ret > 0)
- ret = -EIO;
- return ERR_PTR(ret);
+ dev_err(ctrl->ctrl.device,
+ "NVME-FC{%d}: failed to schedule initial connect\n",
+ ctrl->cnum);
+ goto fail_ctrl;
}
- nvme_get_ctrl(&ctrl->ctrl);
+ flush_delayed_work(&ctrl->connect_work);
dev_info(ctrl->ctrl.device,
"NVME-FC{%d}: new ctrl: NQN \"%s\"\n",
return &ctrl->ctrl;
+fail_ctrl:
+ nvme_change_ctrl_state(&ctrl->ctrl, NVME_CTRL_DELETING);
+ cancel_work_sync(&ctrl->ctrl.reset_work);
+ cancel_delayed_work_sync(&ctrl->connect_work);
+
+ ctrl->ctrl.opts = NULL;
+
+ /* initiate nvme ctrl ref counting teardown */
+ nvme_uninit_ctrl(&ctrl->ctrl);
+
+ /* Remove core ctrl ref. */
+ nvme_put_ctrl(&ctrl->ctrl);
+
+ /* as we're past the point where we transition to the ref
+ * counting teardown path, if we return a bad pointer here,
+ * the calling routine, thinking it's prior to the
+ * transition, will do an rport put. Since the teardown
+ * path also does a rport put, we do an extra get here to
+ * so proper order/teardown happens.
+ */
+ nvme_fc_rport_get(rport);
+
+ return ERR_PTR(-EIO);
+
out_cleanup_admin_q:
blk_cleanup_queue(ctrl->ctrl.admin_q);
out_free_admin_tag_set:
*/
#include <linux/moduleparam.h>
+#include <trace/events/block.h>
#include "nvme.h"
static bool multipath = true;
if (likely(ns)) {
bio->bi_disk = ns->disk;
bio->bi_opf |= REQ_NVME_MPATH;
+ trace_block_bio_remap(bio->bi_disk->queue, bio,
+ disk_devt(ns->head->disk),
+ bio->bi_iter.bi_sector);
ret = direct_make_request(bio);
} else if (!list_empty_careful(&head->list)) {
dev_warn_ratelimited(dev, "no path available - requeuing I/O\n");
void (*submit_async_event)(struct nvme_ctrl *ctrl);
void (*delete_ctrl)(struct nvme_ctrl *ctrl);
int (*get_address)(struct nvme_ctrl *ctrl, char *buf, int size);
- int (*reinit_request)(void *data, struct request *rq);
void (*stop_ctrl)(struct nvme_ctrl *ctrl);
};
void nvme_wait_freeze(struct nvme_ctrl *ctrl);
void nvme_wait_freeze_timeout(struct nvme_ctrl *ctrl, long timeout);
void nvme_start_freeze(struct nvme_ctrl *ctrl);
-int nvme_reinit_tagset(struct nvme_ctrl *ctrl, struct blk_mq_tag_set *set);
#define NVME_QID_ANY -1
struct request *nvme_alloc_request(struct request_queue *q,
count = ib_dma_map_sg(ibdev, req->sg_table.sgl, req->nents,
rq_data_dir(rq) == WRITE ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
if (unlikely(count <= 0)) {
- sg_free_table_chained(&req->sg_table, true);
- return -EIO;
+ ret = -EIO;
+ goto out_free_table;
}
if (count == 1) {
if (rq_data_dir(rq) == WRITE && nvme_rdma_queue_idx(queue) &&
blk_rq_payload_bytes(rq) <=
- nvme_rdma_inline_data_size(queue))
- return nvme_rdma_map_sg_inline(queue, req, c);
+ nvme_rdma_inline_data_size(queue)) {
+ ret = nvme_rdma_map_sg_inline(queue, req, c);
+ goto out;
+ }
- if (dev->pd->flags & IB_PD_UNSAFE_GLOBAL_RKEY)
- return nvme_rdma_map_sg_single(queue, req, c);
+ if (dev->pd->flags & IB_PD_UNSAFE_GLOBAL_RKEY) {
+ ret = nvme_rdma_map_sg_single(queue, req, c);
+ goto out;
+ }
}
- return nvme_rdma_map_sg_fr(queue, req, c, count);
+ ret = nvme_rdma_map_sg_fr(queue, req, c, count);
+out:
+ if (unlikely(ret))
+ goto out_unmap_sg;
+
+ return 0;
+
+out_unmap_sg:
+ ib_dma_unmap_sg(ibdev, req->sg_table.sgl,
+ req->nents, rq_data_dir(rq) ==
+ WRITE ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+out_free_table:
+ sg_free_table_chained(&req->sg_table, true);
+ return ret;
}
static void nvme_rdma_send_done(struct ib_cq *cq, struct ib_wc *wc)
struct nvme_rdma_qe *sqe = &req->sqe;
struct nvme_command *c = sqe->data;
struct ib_device *dev;
+ bool queue_ready = test_bit(NVME_RDMA_Q_LIVE, &queue->flags);
blk_status_t ret;
int err;
WARN_ON_ONCE(rq->tag < 0);
- ret = nvmf_check_if_ready(&queue->ctrl->ctrl, rq,
- test_bit(NVME_RDMA_Q_LIVE, &queue->flags), true);
- if (unlikely(ret))
- return ret;
+ if (!nvmf_check_ready(&queue->ctrl->ctrl, rq, queue_ready))
+ return nvmf_fail_nonready_command(rq);
dev = queue->device->dev;
ib_dma_sync_single_for_cpu(dev, sqe->dma,
else
status = nvmet_get_smart_log_nsid(req, log);
if (status)
- goto out;
+ goto out_free_log;
status = nvmet_copy_to_sgl(req, 0, log, sizeof(*log));
+out_free_log:
+ kfree(log);
out:
nvmet_req_complete(req, status);
}
struct nvme_loop_queue *queue = hctx->driver_data;
struct request *req = bd->rq;
struct nvme_loop_iod *iod = blk_mq_rq_to_pdu(req);
+ bool queue_ready = test_bit(NVME_LOOP_Q_LIVE, &queue->flags);
blk_status_t ret;
- ret = nvmf_check_if_ready(&queue->ctrl->ctrl, req,
- test_bit(NVME_LOOP_Q_LIVE, &queue->flags), true);
- if (unlikely(ret))
- return ret;
+ if (!nvmf_check_ready(&queue->ctrl->ctrl, req, queue_ready))
+ return nvmf_fail_nonready_command(req);
ret = nvme_setup_cmd(ns, req, &iod->cmd);
if (ret)
drive, PLIP link (Parallel Line Internet Protocol is mainly used to
create a mini network by connecting the parallel ports of two local
machines) etc., then you need to say Y here; please read
- <file:Documentation/parport.txt> and
+ <file:Documentation/admin-guide/parport.rst> and
<file:drivers/parport/BUGS-parport>.
For extensive information about drivers for many devices attaching
the module will be called parport.
If you have more than one parallel port and want to specify which
port and IRQ to be used by this driver at module load time, take a
- look at <file:Documentation/parport.txt>.
+ look at <file:Documentation/admin-guide/parport.rst>.
If unsure, say Y.
As well as actually having a FIFO, or DMA capability, the kernel
will need to know which IRQ the parallel port has. By default,
parallel port interrupts will not be used, and so neither will the
- FIFO. See <file:Documentation/parport.txt> to find out how to
+ FIFO. See <file:Documentation/admin-guide/parport.rst> to find out how to
specify which IRQ/DMA to use.
config PARPORT_PC_SUPERIO
#define MLXREG_HOTPLUG_RST_CNTR 3
#define MLXREG_HOTPLUG_ATTRS_MAX 24
+#define MLXREG_HOTPLUG_NOT_ASSERT 3
/**
* struct mlxreg_hotplug_priv_data - platform private data:
* @irq: platform device interrupt number;
+ * @dev: basic device;
* @pdev: platform device;
* @plat: platform data;
- * @dwork: delayed work template;
+ * @regmap: register map handle;
+ * @dwork_irq: delayed work template;
* @lock: spin lock;
* @hwmon: hwmon device;
* @mlxreg_hotplug_attr: sysfs attributes array;
* @cell: location of top aggregation interrupt register;
* @mask: top aggregation interrupt common mask;
* @aggr_cache: last value of aggregation register status;
+ * @after_probe: flag indication probing completion;
+ * @not_asserted: number of entries in workqueue with no signal assertion;
*/
struct mlxreg_hotplug_priv_data {
int irq;
struct mlxreg_hotplug_platform_data *plat;
struct regmap *regmap;
struct delayed_work dwork_irq;
- struct delayed_work dwork;
spinlock_t lock; /* sync with interrupt */
struct device *hwmon;
struct attribute *mlxreg_hotplug_attr[MLXREG_HOTPLUG_ATTRS_MAX + 1];
u32 mask;
u32 aggr_cache;
bool after_probe;
+ u8 not_asserted;
};
static int mlxreg_hotplug_device_create(struct mlxreg_hotplug_priv_data *priv,
aggr_asserted = priv->aggr_cache ^ regval;
priv->aggr_cache = regval;
+ /*
+ * Handler is invoked, but no assertion is detected at top aggregation
+ * status level. Set aggr_asserted to mask value to allow handler extra
+ * run over all relevant signals to recover any missed signal.
+ */
+ if (priv->not_asserted == MLXREG_HOTPLUG_NOT_ASSERT) {
+ priv->not_asserted = 0;
+ aggr_asserted = pdata->mask;
+ }
+ if (!aggr_asserted)
+ goto unmask_event;
+
/* Handle topology and health configuration changes. */
for (i = 0; i < pdata->counter; i++, item++) {
if (aggr_asserted & item->aggr_mask) {
}
}
- if (aggr_asserted) {
- spin_lock_irqsave(&priv->lock, flags);
+ spin_lock_irqsave(&priv->lock, flags);
- /*
- * It is possible, that some signals have been inserted, while
- * interrupt has been masked by mlxreg_hotplug_work_handler.
- * In this case such signals will be missed. In order to handle
- * these signals delayed work is canceled and work task
- * re-scheduled for immediate execution. It allows to handle
- * missed signals, if any. In other case work handler just
- * validates that no new signals have been received during
- * masking.
- */
- cancel_delayed_work(&priv->dwork_irq);
- schedule_delayed_work(&priv->dwork_irq, 0);
+ /*
+ * It is possible, that some signals have been inserted, while
+ * interrupt has been masked by mlxreg_hotplug_work_handler. In this
+ * case such signals will be missed. In order to handle these signals
+ * delayed work is canceled and work task re-scheduled for immediate
+ * execution. It allows to handle missed signals, if any. In other case
+ * work handler just validates that no new signals have been received
+ * during masking.
+ */
+ cancel_delayed_work(&priv->dwork_irq);
+ schedule_delayed_work(&priv->dwork_irq, 0);
- spin_unlock_irqrestore(&priv->lock, flags);
+ spin_unlock_irqrestore(&priv->lock, flags);
- return;
- }
+ return;
+unmask_event:
+ priv->not_asserted++;
/* Unmask aggregation event (no need acknowledge). */
ret = regmap_write(priv->regmap, pdata->cell +
MLXREG_HOTPLUG_AGGR_MASK_OFF, pdata->mask);
function keys, wireless LED, LCD backlight level.
It may also provide some sysfs files described in
- <file:Documentation/ABI/testing/sysfs-platform-samsung-laptop>
+ <file:Documentation/ABI/testing/sysfs-driver-samsung-laptop>
To compile this driver as a module, choose M here: the module
will be called samsung-laptop.
{KE_IGNORE, 0x83, {KEY_TOUCHPAD_TOGGLE} },
{KE_KEY, 0x85, {KEY_TOUCHPAD_TOGGLE} },
{KE_KEY, 0x86, {KEY_WLAN} },
+ {KE_KEY, 0x87, {KEY_POWER} },
{KE_END, 0}
};
return gmux_set_discrete_state(apple_gmux_data, state);
}
-static int gmux_get_client_id(struct pci_dev *pdev)
+static enum vga_switcheroo_client_id gmux_get_client_id(struct pci_dev *pdev)
{
/*
* Early Macbook Pros with switchable graphics use nvidia
int idx)
{
struct device *dev = container_of(kobj, struct device, kobj);
- struct platform_device *pdev = to_platform_device(dev);
- struct asus_laptop *asus = platform_get_drvdata(pdev);
+ struct asus_laptop *asus = dev_get_drvdata(dev);
acpi_handle handle = asus->handle;
bool supported;
acpi_handle_err(handle,
"Failed to eval method %s, param %#x (%d)\n",
method, param, s);
- acpi_handle_debug(handle, "%s returned %#x\n", method, (uint) ret);
+ acpi_handle_debug(handle, "%s returned %#llx\n", method, ret);
return ret;
}
struct attribute *attr, int idx)
{
struct device *dev = container_of(kobj, struct device, kobj);
- struct platform_device *pdev = to_platform_device(dev);
- struct asus_wmi *asus = platform_get_drvdata(pdev);
+ struct asus_wmi *asus = dev_get_drvdata(dev);
bool ok = true;
int devid = -1;
struct quirk_entry {
bool touchpad_led;
bool kbd_led_levels_off_1;
+ bool kbd_missing_ac_tag;
bool needs_kbd_timeouts;
/*
.kbd_timeouts = { 0, 5, 15, 60, 5 * 60, 15 * 60, -1 },
};
+static struct quirk_entry quirk_dell_xps13_9370 = {
+ .kbd_missing_ac_tag = true,
+};
+
static struct quirk_entry quirk_dell_latitude_e6410 = {
.kbd_led_levels_off_1 = true,
};
},
.driver_data = &quirk_dell_xps13_9333,
},
+ {
+ .callback = dmi_matched,
+ .ident = "Dell XPS 13 9370",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "XPS 13 9370"),
+ },
+ .driver_data = &quirk_dell_xps13_9370,
+ },
{
.callback = dmi_matched,
.ident = "Dell Latitude E6410",
* timeout value which is shared for both battery and AC power
* settings. So do not try to set AC values on old models.
*/
- if (dell_smbios_find_token(KBD_LED_AC_TOKEN))
+ if ((quirks && quirks->kbd_missing_ac_tag) ||
+ dell_smbios_find_token(KBD_LED_AC_TOKEN))
kbd_timeout_ac_supported = true;
kbd_get_state(&state);
static int __init dell_smbios_init(void)
{
- const struct dmi_device *valid;
int ret, wmi, smm;
- valid = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, "Dell System", NULL);
- if (!valid) {
+ if (!dmi_find_device(DMI_DEV_TYPE_OEM_STRING, "Dell System", NULL) &&
+ !dmi_find_device(DMI_DEV_TYPE_OEM_STRING, "www.dell.com", NULL)) {
pr_err("Unable to run on non-Dell system\n");
return -ENODEV;
}
[18] = KEY_PROG1,
[19] = KEY_BRIGHTNESSDOWN,
[20] = KEY_BRIGHTNESSUP,
- [21] = KEY_UNKNOWN,
+ [21] = KEY_BRIGHTNESS_AUTO,
[22] = KEY_KBDILLUMTOGGLE,
[23] = KEY_UNKNOWN,
[24] = KEY_SWITCHVIDEOMODE,
* override them.
*/
static const struct key_entry dell_wmi_keymap_type_0010[] = {
+ /* Fn-lock switched to function keys */
+ { KE_IGNORE, 0x0, { KEY_RESERVED } },
+
+ /* Fn-lock switched to multimedia keys */
+ { KE_IGNORE, 0x1, { KEY_RESERVED } },
+
/* Mic mute */
{ KE_KEY, 0x150, { KEY_MICMUTE } },
{ KE_KEY, 0x851, { KEY_PROG2 } },
{ KE_KEY, 0x852, { KEY_PROG3 } },
+ /*
+ * Radio disable (notify only -- there is no model for which the
+ * WMI event is supposed to trigger an action).
+ */
+ { KE_IGNORE, 0xe008, { KEY_RFKILL } },
+
+ /* Fn-lock */
+ { KE_IGNORE, 0xe035, { KEY_RESERVED } },
};
/*
#define FLAG_RFKILL BIT(5)
#define FLAG_LID BIT(8)
#define FLAG_DOCK BIT(9)
+#define FLAG_TOUCHPAD_TOGGLE BIT(26)
+#define FLAG_MICMUTE BIT(29)
+#define FLAG_SOFTKEYS (FLAG_RFKILL | FLAG_TOUCHPAD_TOGGLE | FLAG_MICMUTE)
/* FUNC interface - LED control */
#define FUNC_LED_OFF BIT(0)
/* ACPI device for hotkey handling */
static const struct key_entry keymap_default[] = {
- { KE_KEY, KEY1_CODE, { KEY_PROG1 } },
- { KE_KEY, KEY2_CODE, { KEY_PROG2 } },
- { KE_KEY, KEY3_CODE, { KEY_PROG3 } },
- { KE_KEY, KEY4_CODE, { KEY_PROG4 } },
- { KE_KEY, KEY5_CODE, { KEY_RFKILL } },
- { KE_KEY, BIT(5), { KEY_RFKILL } },
- { KE_KEY, BIT(26), { KEY_TOUCHPAD_TOGGLE } },
- { KE_KEY, BIT(29), { KEY_MICMUTE } },
+ { KE_KEY, KEY1_CODE, { KEY_PROG1 } },
+ { KE_KEY, KEY2_CODE, { KEY_PROG2 } },
+ { KE_KEY, KEY3_CODE, { KEY_PROG3 } },
+ { KE_KEY, KEY4_CODE, { KEY_PROG4 } },
+ { KE_KEY, KEY5_CODE, { KEY_RFKILL } },
+ /* Soft keys read from status flags */
+ { KE_KEY, FLAG_RFKILL, { KEY_RFKILL } },
+ { KE_KEY, FLAG_TOUCHPAD_TOGGLE, { KEY_TOUCHPAD_TOGGLE } },
+ { KE_KEY, FLAG_MICMUTE, { KEY_MICMUTE } },
{ KE_END, 0 }
};
static void acpi_fujitsu_laptop_notify(struct acpi_device *device, u32 event)
{
struct fujitsu_laptop *priv = acpi_driver_data(device);
- int scancode, i = 0, ret;
+ unsigned long flags;
+ int scancode, i = 0;
unsigned int irb;
if (event != ACPI_FUJITSU_NOTIFY_CODE) {
"Unknown GIRB result [%x]\n", irb);
}
- /* On some models (first seen on the Skylake-based Lifebook
- * E736/E746/E756), the touchpad toggle hotkey (Fn+F4) is
- * handled in software; its state is queried using FUNC_FLAGS
+ /*
+ * First seen on the Skylake-based Lifebook E736/E746/E756), the
+ * touchpad toggle hotkey (Fn+F4) is handled in software. Other models
+ * have since added additional "soft keys". These are reported in the
+ * status flags queried using FUNC_FLAGS.
*/
- if (priv->flags_supported & (BIT(5) | BIT(26) | BIT(29))) {
- ret = call_fext_func(device, FUNC_FLAGS, 0x1, 0x0, 0x0);
- if (ret & BIT(5))
- sparse_keymap_report_event(priv->input,
- BIT(5), 1, true);
- if (ret & BIT(26))
- sparse_keymap_report_event(priv->input,
- BIT(26), 1, true);
- if (ret & BIT(29))
- sparse_keymap_report_event(priv->input,
- BIT(29), 1, true);
+ if (priv->flags_supported & (FLAG_SOFTKEYS)) {
+ flags = call_fext_func(device, FUNC_FLAGS, 0x1, 0x0, 0x0);
+ flags &= (FLAG_SOFTKEYS);
+ for_each_set_bit(i, &flags, BITS_PER_LONG)
+ sparse_keymap_report_event(priv->input, BIT(i), 1, true);
}
}
#define IDEAPAD_RFKILL_DEV_NUM (3)
#define BM_CONSERVATION_BIT (5)
+#define HA_FNLOCK_BIT (10)
#define CFG_BT_BIT (16)
#define CFG_3G_BIT (17)
enum {
BMCMD_CONSERVATION_ON = 3,
BMCMD_CONSERVATION_OFF = 5,
+ HACMD_FNLOCK_ON = 0xe,
+ HACMD_FNLOCK_OFF = 0xf,
};
enum {
return result;
}
-static int method_sbmc(acpi_handle handle, int cmd)
+static int method_int1(acpi_handle handle, char *method, int cmd)
{
acpi_status status;
- status = acpi_execute_simple_method(handle, "SBMC", cmd);
+ status = acpi_execute_simple_method(handle, method, cmd);
return ACPI_FAILURE(status) ? -1 : 0;
}
if (ret)
return ret;
- ret = method_sbmc(priv->adev->handle, state ?
+ ret = method_int1(priv->adev->handle, "SBMC", state ?
BMCMD_CONSERVATION_ON :
BMCMD_CONSERVATION_OFF);
if (ret < 0)
static DEVICE_ATTR_RW(conservation_mode);
+static ssize_t fn_lock_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct ideapad_private *priv = dev_get_drvdata(dev);
+ unsigned long result;
+ int hals;
+ int fail = read_method_int(priv->adev->handle, "HALS", &hals);
+
+ if (fail)
+ return sprintf(buf, "-1\n");
+
+ result = hals;
+ return sprintf(buf, "%u\n", test_bit(HA_FNLOCK_BIT, &result));
+}
+
+static ssize_t fn_lock_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct ideapad_private *priv = dev_get_drvdata(dev);
+ bool state;
+ int ret;
+
+ ret = kstrtobool(buf, &state);
+ if (ret)
+ return ret;
+
+ ret = method_int1(priv->adev->handle, "SALS", state ?
+ HACMD_FNLOCK_ON :
+ HACMD_FNLOCK_OFF);
+ if (ret < 0)
+ return -EIO;
+ return count;
+}
+
+static DEVICE_ATTR_RW(fn_lock);
+
+
static struct attribute *ideapad_attributes[] = {
&dev_attr_camera_power.attr,
&dev_attr_fan_mode.attr,
&dev_attr_touchpad.attr,
&dev_attr_conservation_mode.attr,
+ &dev_attr_fn_lock.attr,
NULL
};
} else if (attr == &dev_attr_conservation_mode.attr) {
supported = acpi_has_method(priv->adev->handle, "GBMD") &&
acpi_has_method(priv->adev->handle, "SBMC");
+ } else if (attr == &dev_attr_fn_lock.attr) {
+ supported = acpi_has_method(priv->adev->handle, "HALS") &&
+ acpi_has_method(priv->adev->handle, "SALS");
} else
supported = true;
DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad Y700-17ISK"),
},
},
+ {
+ .ident = "Lenovo ideapad MIIX 720-12IKB",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "MIIX 720-12IKB"),
+ },
+ },
{
.ident = "Lenovo Legion Y520-15IKBN",
.matches = {
DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo YOGA 920-13IKB"),
},
},
+ {
+ .ident = "Lenovo Zhaoyang E42-80",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "ZHAOYANG E42-80"),
+ },
+ },
{}
};
if (cmd == IPC_I2C_READ) {
writel(addr, scu->i2c_base + IPC_I2C_CNTRL_ADDR);
/* Write not getting updated without delay */
- mdelay(1);
+ usleep_range(1000, 2000);
*data = readl(scu->i2c_base + I2C_DATA_ADDR);
} else if (cmd == IPC_I2C_WRITE) {
writel(*data, scu->i2c_base + I2C_DATA_ADDR);
- mdelay(1);
+ usleep_range(1000, 2000);
writel(addr, scu->i2c_base + IPC_I2C_CNTRL_ADDR);
} else {
dev_err(scu->dev,
/* LPC bus IO offsets */
#define MLXPLAT_CPLD_LPC_I2C_BASE_ADRR 0x2000
#define MLXPLAT_CPLD_LPC_REG_BASE_ADRR 0x2500
+#define MLXPLAT_CPLD_LPC_REG_LED1_OFFSET 0x20
+#define MLXPLAT_CPLD_LPC_REG_LED2_OFFSET 0x21
+#define MLXPLAT_CPLD_LPC_REG_LED3_OFFSET 0x22
+#define MLXPLAT_CPLD_LPC_REG_LED4_OFFSET 0x23
+#define MLXPLAT_CPLD_LPC_REG_LED5_OFFSET 0x24
#define MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET 0x3a
#define MLXPLAT_CPLD_LPC_REG_AGGR_MASK_OFFSET 0x3b
#define MLXPLAT_CPLD_LPC_REG_AGGRLO_OFFSET 0x40
#define MLXPLAT_CPLD_PWR_MASK GENMASK(1, 0)
#define MLXPLAT_CPLD_FAN_MASK GENMASK(3, 0)
#define MLXPLAT_CPLD_FAN_NG_MASK GENMASK(5, 0)
+#define MLXPLAT_CPLD_LED_LO_NIBBLE_MASK GENMASK(7, 4)
+#define MLXPLAT_CPLD_LED_HI_NIBBLE_MASK GENMASK(3, 0)
/* Default I2C parent bus number */
#define MLXPLAT_CPLD_PHYS_ADAPTER_DEF_NR 1
* @pdev_i2c - i2c controller platform device
* @pdev_mux - array of mux platform devices
* @pdev_hotplug - hotplug platform devices
+ * @pdev_led - led platform devices
*/
struct mlxplat_priv {
struct platform_device *pdev_i2c;
struct platform_device *pdev_mux[MLXPLAT_CPLD_LPC_MUX_DEVS];
struct platform_device *pdev_hotplug;
+ struct platform_device *pdev_led;
};
/* Regions for LPC I2C controller and LPC base register space */
.mask_low = MLXPLAT_CPLD_LOW_AGGR_MASK_LOW,
};
+/* Platform led default data */
+static struct mlxreg_core_data mlxplat_mlxcpld_default_led_data[] = {
+ {
+ .label = "status:green",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED1_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK,
+ },
+ {
+ .label = "status:red",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED1_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK
+ },
+ {
+ .label = "psu:green",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED1_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_HI_NIBBLE_MASK,
+ },
+ {
+ .label = "psu:red",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED1_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_HI_NIBBLE_MASK,
+ },
+ {
+ .label = "fan1:green",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED2_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK,
+ },
+ {
+ .label = "fan1:red",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED2_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK,
+ },
+ {
+ .label = "fan2:green",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED2_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_HI_NIBBLE_MASK,
+ },
+ {
+ .label = "fan2:red",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED2_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_HI_NIBBLE_MASK,
+ },
+ {
+ .label = "fan3:green",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED3_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK,
+ },
+ {
+ .label = "fan3:red",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED3_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK,
+ },
+ {
+ .label = "fan4:green",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED3_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_HI_NIBBLE_MASK,
+ },
+ {
+ .label = "fan4:red",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED3_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_HI_NIBBLE_MASK,
+ },
+};
+
+static struct mlxreg_core_platform_data mlxplat_default_led_data = {
+ .data = mlxplat_mlxcpld_default_led_data,
+ .counter = ARRAY_SIZE(mlxplat_mlxcpld_default_led_data),
+};
+
+/* Platform led MSN21xx system family data */
+static struct mlxreg_core_data mlxplat_mlxcpld_msn21xx_led_data[] = {
+ {
+ .label = "status:green",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED1_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK,
+ },
+ {
+ .label = "status:red",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED1_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK
+ },
+ {
+ .label = "fan:green",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED2_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK,
+ },
+ {
+ .label = "fan:red",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED2_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK,
+ },
+ {
+ .label = "psu1:green",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED4_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK,
+ },
+ {
+ .label = "psu1:red",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED4_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK,
+ },
+ {
+ .label = "psu2:green",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED4_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_HI_NIBBLE_MASK,
+ },
+ {
+ .label = "psu2:red",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED4_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_HI_NIBBLE_MASK,
+ },
+ {
+ .label = "uid:blue",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED5_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK,
+ },
+};
+
+static struct mlxreg_core_platform_data mlxplat_msn21xx_led_data = {
+ .data = mlxplat_mlxcpld_msn21xx_led_data,
+ .counter = ARRAY_SIZE(mlxplat_mlxcpld_msn21xx_led_data),
+};
+
+/* Platform led for default data for 200GbE systems */
+static struct mlxreg_core_data mlxplat_mlxcpld_default_ng_led_data[] = {
+ {
+ .label = "status:green",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED1_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK,
+ },
+ {
+ .label = "status:orange",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED1_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK
+ },
+ {
+ .label = "psu:green",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED1_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_HI_NIBBLE_MASK,
+ },
+ {
+ .label = "psu:orange",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED1_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_HI_NIBBLE_MASK,
+ },
+ {
+ .label = "fan1:green",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED2_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK,
+ },
+ {
+ .label = "fan1:orange",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED2_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK,
+ },
+ {
+ .label = "fan2:green",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED2_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_HI_NIBBLE_MASK,
+ },
+ {
+ .label = "fan2:orange",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED2_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_HI_NIBBLE_MASK,
+ },
+ {
+ .label = "fan3:green",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED3_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK,
+ },
+ {
+ .label = "fan3:orange",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED3_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK,
+ },
+ {
+ .label = "fan4:green",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED3_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_HI_NIBBLE_MASK,
+ },
+ {
+ .label = "fan4:orange",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED3_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_HI_NIBBLE_MASK,
+ },
+ {
+ .label = "fan5:green",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED4_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK,
+ },
+ {
+ .label = "fan5:orange",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED4_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK,
+ },
+ {
+ .label = "fan6:green",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED4_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_HI_NIBBLE_MASK,
+ },
+ {
+ .label = "fan6:orange",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED4_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_HI_NIBBLE_MASK,
+ },
+};
+
+static struct mlxreg_core_platform_data mlxplat_default_ng_led_data = {
+ .data = mlxplat_mlxcpld_default_ng_led_data,
+ .counter = ARRAY_SIZE(mlxplat_mlxcpld_default_ng_led_data),
+};
+
+
static bool mlxplat_mlxcpld_writeable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
+ case MLXPLAT_CPLD_LPC_REG_LED1_OFFSET:
+ case MLXPLAT_CPLD_LPC_REG_LED2_OFFSET:
+ case MLXPLAT_CPLD_LPC_REG_LED3_OFFSET:
+ case MLXPLAT_CPLD_LPC_REG_LED4_OFFSET:
+ case MLXPLAT_CPLD_LPC_REG_LED5_OFFSET:
case MLXPLAT_CPLD_LPC_REG_AGGR_MASK_OFFSET:
case MLXPLAT_CPLD_LPC_REG_AGGRLO_MASK_OFFSET:
case MLXPLAT_CPLD_LPC_REG_PSU_EVENT_OFFSET:
static bool mlxplat_mlxcpld_readable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
+ case MLXPLAT_CPLD_LPC_REG_LED1_OFFSET:
+ case MLXPLAT_CPLD_LPC_REG_LED2_OFFSET:
+ case MLXPLAT_CPLD_LPC_REG_LED3_OFFSET:
+ case MLXPLAT_CPLD_LPC_REG_LED4_OFFSET:
+ case MLXPLAT_CPLD_LPC_REG_LED5_OFFSET:
case MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET:
case MLXPLAT_CPLD_LPC_REG_AGGR_MASK_OFFSET:
case MLXPLAT_CPLD_LPC_REG_AGGRLO_OFFSET:
static bool mlxplat_mlxcpld_volatile_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
+ case MLXPLAT_CPLD_LPC_REG_LED1_OFFSET:
+ case MLXPLAT_CPLD_LPC_REG_LED2_OFFSET:
+ case MLXPLAT_CPLD_LPC_REG_LED3_OFFSET:
+ case MLXPLAT_CPLD_LPC_REG_LED4_OFFSET:
+ case MLXPLAT_CPLD_LPC_REG_LED5_OFFSET:
case MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET:
case MLXPLAT_CPLD_LPC_REG_AGGR_MASK_OFFSET:
case MLXPLAT_CPLD_LPC_REG_AGGRLO_OFFSET:
static struct platform_device *mlxplat_dev;
static struct mlxreg_core_hotplug_platform_data *mlxplat_hotplug;
+static struct mlxreg_core_platform_data *mlxplat_led;
static int __init mlxplat_dmi_default_matched(const struct dmi_system_id *dmi)
{
mlxplat_hotplug = &mlxplat_mlxcpld_default_data;
mlxplat_hotplug->deferred_nr =
mlxplat_default_channels[i - 1][MLXPLAT_CPLD_GRP_CHNL_NUM - 1];
+ mlxplat_led = &mlxplat_default_led_data;
return 1;
};
mlxplat_hotplug = &mlxplat_mlxcpld_msn21xx_data;
mlxplat_hotplug->deferred_nr =
mlxplat_msn21xx_channels[MLXPLAT_CPLD_GRP_CHNL_NUM - 1];
+ mlxplat_led = &mlxplat_msn21xx_led_data;
return 1;
};
mlxplat_hotplug = &mlxplat_mlxcpld_msn274x_data;
mlxplat_hotplug->deferred_nr =
mlxplat_msn21xx_channels[MLXPLAT_CPLD_GRP_CHNL_NUM - 1];
+ mlxplat_led = &mlxplat_default_led_data;
return 1;
};
mlxplat_hotplug = &mlxplat_mlxcpld_msn201x_data;
mlxplat_hotplug->deferred_nr =
mlxplat_default_channels[i - 1][MLXPLAT_CPLD_GRP_CHNL_NUM - 1];
+ mlxplat_led = &mlxplat_default_ng_led_data;
return 1;
};
mlxplat_hotplug = &mlxplat_mlxcpld_default_ng_data;
mlxplat_hotplug->deferred_nr =
mlxplat_msn21xx_channels[MLXPLAT_CPLD_GRP_CHNL_NUM - 1];
+ mlxplat_led = &mlxplat_msn21xx_led_data;
return 1;
};
DMI_MATCH(DMI_PRODUCT_NAME, "SN34"),
},
},
+ {
+ .callback = mlxplat_dmi_default_matched,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_NAME, "VMOD0001"),
+ },
+ },
+ {
+ .callback = mlxplat_dmi_msn21xx_matched,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_NAME, "VMOD0002"),
+ },
+ },
+ {
+ .callback = mlxplat_dmi_msn274x_matched,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_NAME, "VMOD0003"),
+ },
+ },
+ {
+ .callback = mlxplat_dmi_msn201x_matched,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_NAME, "VMOD0004"),
+ },
+ },
+ {
+ .callback = mlxplat_dmi_qmb7xx_matched,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_NAME, "VMOD0005"),
+ },
+ },
{ }
};
goto fail_platform_mux_register;
}
+ /* Add LED driver. */
+ mlxplat_led->regmap = mlxplat_hotplug->regmap;
+ priv->pdev_led = platform_device_register_resndata(
+ &mlxplat_dev->dev, "leds-mlxreg",
+ PLATFORM_DEVID_NONE, NULL, 0,
+ mlxplat_led, sizeof(*mlxplat_led));
+ if (IS_ERR(priv->pdev_led)) {
+ err = PTR_ERR(priv->pdev_led);
+ goto fail_platform_hotplug_register;
+ }
+
/* Sync registers with hardware. */
regcache_mark_dirty(mlxplat_hotplug->regmap);
err = regcache_sync(mlxplat_hotplug->regmap);
if (err)
- goto fail_platform_hotplug_register;
+ goto fail_platform_led_register;
return 0;
+fail_platform_led_register:
+ platform_device_unregister(priv->pdev_led);
fail_platform_hotplug_register:
platform_device_unregister(priv->pdev_hotplug);
fail_platform_mux_register:
struct mlxplat_priv *priv = platform_get_drvdata(mlxplat_dev);
int i;
+ platform_device_unregister(priv->pdev_led);
platform_device_unregister(priv->pdev_hotplug);
for (i = ARRAY_SIZE(mlxplat_mux_data) - 1; i >= 0 ; i--)
struct attribute *attr, int idx)
{
struct device *dev = container_of(kobj, struct device, kobj);
- struct platform_device *pdev = to_platform_device(dev);
- struct samsung_laptop *samsung = platform_get_drvdata(pdev);
+ struct samsung_laptop *samsung = dev_get_drvdata(dev);
bool ok = true;
if (attr == &dev_attr_performance_level.attr)
.properties = jumper_ezpad_mini3_props,
};
+static const struct property_entry jumper_ezpad_6_pro_props[] = {
+ PROPERTY_ENTRY_U32("touchscreen-size-x", 1980),
+ PROPERTY_ENTRY_U32("touchscreen-size-y", 1500),
+ PROPERTY_ENTRY_STRING("firmware-name", "gsl3692-jumper-ezpad-6-pro.fw"),
+ PROPERTY_ENTRY_U32("silead,max-fingers", 10),
+ PROPERTY_ENTRY_BOOL("silead,home-button"),
+ { }
+};
+
+static const struct silead_ts_dmi_data jumper_ezpad_6_pro_data = {
+ .acpi_name = "MSSL1680:00",
+ .properties = jumper_ezpad_6_pro_props,
+};
+
static const struct property_entry dexp_ursus_7w_props[] = {
PROPERTY_ENTRY_U32("touchscreen-size-x", 890),
PROPERTY_ENTRY_U32("touchscreen-size-y", 630),
.properties = pipo_w2s_props,
};
-static const struct property_entry pov_mobii_wintab_p800w_props[] = {
+static const struct property_entry pov_mobii_wintab_p800w_v20_props[] = {
+ PROPERTY_ENTRY_U32("touchscreen-min-x", 32),
+ PROPERTY_ENTRY_U32("touchscreen-min-y", 16),
+ PROPERTY_ENTRY_U32("touchscreen-size-x", 1692),
+ PROPERTY_ENTRY_U32("touchscreen-size-y", 1146),
+ PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"),
+ PROPERTY_ENTRY_STRING("firmware-name",
+ "gsl3680-pov-mobii-wintab-p800w-v20.fw"),
+ PROPERTY_ENTRY_U32("silead,max-fingers", 10),
+ PROPERTY_ENTRY_BOOL("silead,home-button"),
+ { }
+};
+
+static const struct silead_ts_dmi_data pov_mobii_wintab_p800w_v20_data = {
+ .acpi_name = "MSSL1680:00",
+ .properties = pov_mobii_wintab_p800w_v20_props,
+};
+
+static const struct property_entry pov_mobii_wintab_p800w_v21_props[] = {
PROPERTY_ENTRY_U32("touchscreen-size-x", 1800),
PROPERTY_ENTRY_U32("touchscreen-size-y", 1150),
PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"),
{ }
};
-static const struct silead_ts_dmi_data pov_mobii_wintab_p800w_data = {
+static const struct silead_ts_dmi_data pov_mobii_wintab_p800w_v21_data = {
.acpi_name = "MSSL1680:00",
- .properties = pov_mobii_wintab_p800w_props,
+ .properties = pov_mobii_wintab_p800w_v21_props,
};
static const struct property_entry itworks_tw891_props[] = {
.properties = teclast_x3_plus_props,
};
+static const struct property_entry onda_v891w_v1_props[] = {
+ PROPERTY_ENTRY_U32("touchscreen-min-x", 46),
+ PROPERTY_ENTRY_U32("touchscreen-min-y", 8),
+ PROPERTY_ENTRY_U32("touchscreen-size-x", 1676),
+ PROPERTY_ENTRY_U32("touchscreen-size-y", 1130),
+ PROPERTY_ENTRY_STRING("firmware-name",
+ "gsl3680-onda-v891w-v1.fw"),
+ PROPERTY_ENTRY_U32("silead,max-fingers", 10),
+ PROPERTY_ENTRY_BOOL("silead,home-button"),
+ { }
+};
+
+static const struct silead_ts_dmi_data onda_v891w_v1_data = {
+ .acpi_name = "MSSL1680:00",
+ .properties = onda_v891w_v1_props,
+};
+
static const struct dmi_system_id silead_ts_dmi_table[] = {
{
/* CUBE iwork8 Air */
DMI_MATCH(DMI_BIOS_VERSION, "jumperx.T87.KFBNEEA"),
},
},
+ {
+ /* Jumper EZpad 6 Pro */
+ .driver_data = (void *)&jumper_ezpad_6_pro_data,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Jumper"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "EZpad"),
+ DMI_MATCH(DMI_BIOS_VERSION, "5.12"),
+ /* Above matches are too generic, add bios-date match */
+ DMI_MATCH(DMI_BIOS_DATE, "08/18/2017"),
+ },
+ },
{
/* DEXP Ursus 7W */
.driver_data = (void *)&dexp_ursus_7w_data,
},
},
{
- /* Point of View mobii wintab p800w */
- .driver_data = (void *)&pov_mobii_wintab_p800w_data,
+ /* Point of View mobii wintab p800w (v2.0) */
+ .driver_data = (void *)&pov_mobii_wintab_p800w_v20_data,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"),
+ DMI_MATCH(DMI_BOARD_NAME, "Aptio CRB"),
+ DMI_MATCH(DMI_BIOS_VERSION, "3BAIR1014"),
+ /* Above matches are too generic, add bios-date match */
+ DMI_MATCH(DMI_BIOS_DATE, "10/24/2014"),
+ },
+ },
+ {
+ /* Point of View mobii wintab p800w (v2.1) */
+ .driver_data = (void *)&pov_mobii_wintab_p800w_v21_data,
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"),
DMI_MATCH(DMI_BOARD_NAME, "Aptio CRB"),
DMI_MATCH(DMI_PRODUCT_NAME, "S806"),
},
},
+ {
+ /* Chuwi Hi8 (H1D_S806_206) */
+ .driver_data = (void *)&chuwi_hi8_data,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Insyde"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "BayTrail"),
+ DMI_MATCH(DMI_BIOS_VERSION, "H1D_S806_206"),
+ },
+ },
{
/* Chuwi Vi8 (CWI506) */
.driver_data = (void *)&chuwi_vi8_data,
DMI_MATCH(DMI_PRODUCT_NAME, "Y8W81"),
},
},
+ {
+ /* ONDA V891w revision P891WBEBV1B00 aka v1 */
+ .driver_data = (void *)&onda_v891w_v1_data,
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "ONDA"),
+ DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONDA Tablet"),
+ DMI_EXACT_MATCH(DMI_BOARD_VERSION, "V001"),
+ /* Exact match, different versions need different fw */
+ DMI_EXACT_MATCH(DMI_BIOS_VERSION, "ONDA.W89EBBN08"),
+ },
+ },
{ },
};
TP_HKEY_EV_ALARM_BAT_XHOT = 0x6012, /* battery critically hot */
TP_HKEY_EV_ALARM_SENSOR_HOT = 0x6021, /* sensor too hot */
TP_HKEY_EV_ALARM_SENSOR_XHOT = 0x6022, /* sensor critically hot */
- TP_HKEY_EV_THM_TABLE_CHANGED = 0x6030, /* thermal table changed */
+ TP_HKEY_EV_THM_TABLE_CHANGED = 0x6030, /* windows; thermal table changed */
+ TP_HKEY_EV_THM_CSM_COMPLETED = 0x6032, /* windows; thermal control set
+ * command completed. Related to
+ * AML DYTC */
+ TP_HKEY_EV_THM_TRANSFM_CHANGED = 0x60F0, /* windows; thermal transformation
+ * changed. Related to AML GMTS */
/* AC-related events */
TP_HKEY_EV_AC_CHANGED = 0x6040, /* AC status changed */
bool *send_acpi_ev,
bool *ignore_acpi_ev)
{
- bool known = true;
-
/* 0x6000-0x6FFF: thermal alarms/notices and keyboard events */
*send_acpi_ev = true;
*ignore_acpi_ev = false;
switch (hkey) {
case TP_HKEY_EV_THM_TABLE_CHANGED:
- pr_info("EC reports that Thermal Table has changed\n");
+ pr_debug("EC reports: Thermal Table has changed\n");
+ /* recommended action: do nothing, we don't have
+ * Lenovo ATM information */
+ return true;
+ case TP_HKEY_EV_THM_CSM_COMPLETED:
+ pr_debug("EC reports: Thermal Control Command set completed (DYTC)\n");
+ /* recommended action: do nothing, we don't have
+ * Lenovo ATM information */
+ return true;
+ case TP_HKEY_EV_THM_TRANSFM_CHANGED:
+ pr_debug("EC reports: Thermal Transformation changed (GMTS)\n");
/* recommended action: do nothing, we don't have
* Lenovo ATM information */
return true;
tpacpi_input_send_tabletsw();
hotkey_tablet_mode_notify_change();
*send_acpi_ev = false;
- break;
+ return true;
case TP_HKEY_EV_PALM_DETECTED:
case TP_HKEY_EV_PALM_UNDETECTED:
return true;
default:
- pr_warn("unknown possible thermal alarm or keyboard event received\n");
- known = false;
+ /* report simply as unknown, no sensor dump */
+ return false;
}
thermal_dump_all_sensors();
-
- return known;
+ return true;
}
static void hotkey_notify(struct ibm_struct *ibm, u32 event)
config PWM_STM32
tristate "STMicroelectronics STM32 PWM"
- depends on MFD_STM32_TIMERS || COMPILE_TEST
+ depends on MFD_STM32_TIMERS
help
Generic PWM framework driver for STM32 SoCs.
#ifdef CONFIG_PM_SLEEP
static int atmel_tcb_pwm_suspend(struct device *dev)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct atmel_tcb_pwm_chip *tcbpwm = platform_get_drvdata(pdev);
+ struct atmel_tcb_pwm_chip *tcbpwm = dev_get_drvdata(dev);
void __iomem *base = tcbpwm->tc->regs;
int i;
static int atmel_tcb_pwm_resume(struct device *dev)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct atmel_tcb_pwm_chip *tcbpwm = platform_get_drvdata(pdev);
+ struct atmel_tcb_pwm_chip *tcbpwm = dev_get_drvdata(dev);
void __iomem *base = tcbpwm->tc->regs;
int i;
return pwm_lpss_remove(lpwm);
}
+static SIMPLE_DEV_PM_OPS(pwm_lpss_platform_pm_ops,
+ pwm_lpss_suspend,
+ pwm_lpss_resume);
+
static const struct acpi_device_id pwm_lpss_acpi_match[] = {
{ "80860F09", (unsigned long)&pwm_lpss_byt_info },
{ "80862288", (unsigned long)&pwm_lpss_bsw_info },
.driver = {
.name = "pwm-lpss",
.acpi_match_table = pwm_lpss_acpi_match,
+ .pm = &pwm_lpss_platform_pm_ops,
},
.probe = pwm_lpss_probe_platform,
.remove = pwm_lpss_remove_platform,
/* Size of each PWM register space if multiple */
#define PWM_SIZE 0x400
+#define MAX_PWMS 4
+
struct pwm_lpss_chip {
struct pwm_chip chip;
void __iomem *regs;
const struct pwm_lpss_boardinfo *info;
+ u32 saved_ctrl[MAX_PWMS];
};
static inline struct pwm_lpss_chip *to_lpwm(struct pwm_chip *chip)
unsigned long c;
int ret;
+ if (WARN_ON(info->npwm > MAX_PWMS))
+ return ERR_PTR(-ENODEV);
+
lpwm = devm_kzalloc(dev, sizeof(*lpwm), GFP_KERNEL);
if (!lpwm)
return ERR_PTR(-ENOMEM);
}
EXPORT_SYMBOL_GPL(pwm_lpss_remove);
+int pwm_lpss_suspend(struct device *dev)
+{
+ struct pwm_lpss_chip *lpwm = dev_get_drvdata(dev);
+ int i;
+
+ for (i = 0; i < lpwm->info->npwm; i++)
+ lpwm->saved_ctrl[i] = readl(lpwm->regs + i * PWM_SIZE + PWM);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pwm_lpss_suspend);
+
+int pwm_lpss_resume(struct device *dev)
+{
+ struct pwm_lpss_chip *lpwm = dev_get_drvdata(dev);
+ int i;
+
+ for (i = 0; i < lpwm->info->npwm; i++)
+ writel(lpwm->saved_ctrl[i], lpwm->regs + i * PWM_SIZE + PWM);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pwm_lpss_resume);
+
MODULE_DESCRIPTION("PWM driver for Intel LPSS");
MODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com>");
MODULE_LICENSE("GPL v2");
struct pwm_lpss_chip *pwm_lpss_probe(struct device *dev, struct resource *r,
const struct pwm_lpss_boardinfo *info);
int pwm_lpss_remove(struct pwm_lpss_chip *lpwm);
+int pwm_lpss_suspend(struct device *dev);
+int pwm_lpss_resume(struct device *dev);
#endif /* __PWM_LPSS_H */
meson->data = of_device_get_match_data(&pdev->dev);
meson->inverter_mask = BIT(meson->chip.npwm) - 1;
- channels = devm_kcalloc(&pdev->dev, meson->chip.npwm, sizeof(*meson),
- GFP_KERNEL);
+ channels = devm_kcalloc(&pdev->dev, meson->chip.npwm,
+ sizeof(*channels), GFP_KERNEL);
if (!channels)
return -ENOMEM;
#ifdef CONFIG_PM_SLEEP
static struct pwm_device *rcar_pwm_dev_to_pwm_dev(struct device *dev)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct rcar_pwm_chip *rcar_pwm = platform_get_drvdata(pdev);
+ struct rcar_pwm_chip *rcar_pwm = dev_get_drvdata(dev);
struct pwm_chip *chip = &rcar_pwm->chip;
return &chip->pwms[0];
static const struct pwm_ops stm32pwm_ops = {
.owner = THIS_MODULE,
.apply = stm32_pwm_apply_locked,
-#if IS_ENABLED(CONFIG_DMA_ENGINE)
- .capture = stm32_pwm_capture,
-#endif
+ .capture = IS_ENABLED(CONFIG_DMA_ENGINE) ? stm32_pwm_capture : NULL,
};
static int stm32_pwm_set_breakinput(struct stm32_pwm *priv,
struct elf32_hdr *ehdr;
phys_addr_t mpss_reloc;
phys_addr_t boot_addr;
- phys_addr_t min_addr = (phys_addr_t)ULLONG_MAX;
+ phys_addr_t min_addr = PHYS_ADDR_MAX;
phys_addr_t max_addr = 0;
bool relocate = false;
char seg_name[10];
device->hosts_dentry = pde;
}
-/*
- * Allocate memory for a channel program with 'cplength' channel
- * command words and 'datasize' additional space. There are two
- * variantes: 1) dasd_kmalloc_request uses kmalloc to get the needed
- * memory and 2) dasd_smalloc_request uses the static ccw memory
- * that gets allocated for each device.
- */
-struct dasd_ccw_req *dasd_kmalloc_request(int magic, int cplength,
- int datasize,
- struct dasd_device *device)
-{
- struct dasd_ccw_req *cqr;
-
- /* Sanity checks */
- BUG_ON(datasize > PAGE_SIZE ||
- (cplength*sizeof(struct ccw1)) > PAGE_SIZE);
-
- cqr = kzalloc(sizeof(struct dasd_ccw_req), GFP_ATOMIC);
- if (cqr == NULL)
- return ERR_PTR(-ENOMEM);
- cqr->cpaddr = NULL;
- if (cplength > 0) {
- cqr->cpaddr = kcalloc(cplength, sizeof(struct ccw1),
- GFP_ATOMIC | GFP_DMA);
- if (cqr->cpaddr == NULL) {
- kfree(cqr);
- return ERR_PTR(-ENOMEM);
- }
- }
- cqr->data = NULL;
- if (datasize > 0) {
- cqr->data = kzalloc(datasize, GFP_ATOMIC | GFP_DMA);
- if (cqr->data == NULL) {
- kfree(cqr->cpaddr);
- kfree(cqr);
- return ERR_PTR(-ENOMEM);
- }
- }
- cqr->magic = magic;
- set_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
- dasd_get_device(device);
- return cqr;
-}
-EXPORT_SYMBOL(dasd_kmalloc_request);
-
-struct dasd_ccw_req *dasd_smalloc_request(int magic, int cplength,
- int datasize,
- struct dasd_device *device)
+struct dasd_ccw_req *dasd_smalloc_request(int magic, int cplength, int datasize,
+ struct dasd_device *device,
+ struct dasd_ccw_req *cqr)
{
unsigned long flags;
- struct dasd_ccw_req *cqr;
- char *data;
- int size;
+ char *data, *chunk;
+ int size = 0;
- size = (sizeof(struct dasd_ccw_req) + 7L) & -8L;
if (cplength > 0)
size += cplength * sizeof(struct ccw1);
if (datasize > 0)
size += datasize;
+ if (!cqr)
+ size += (sizeof(*cqr) + 7L) & -8L;
+
spin_lock_irqsave(&device->mem_lock, flags);
- cqr = (struct dasd_ccw_req *)
- dasd_alloc_chunk(&device->ccw_chunks, size);
+ data = chunk = dasd_alloc_chunk(&device->ccw_chunks, size);
spin_unlock_irqrestore(&device->mem_lock, flags);
- if (cqr == NULL)
+ if (!chunk)
return ERR_PTR(-ENOMEM);
- memset(cqr, 0, sizeof(struct dasd_ccw_req));
- data = (char *) cqr + ((sizeof(struct dasd_ccw_req) + 7L) & -8L);
- cqr->cpaddr = NULL;
+ if (!cqr) {
+ cqr = (void *) data;
+ data += (sizeof(*cqr) + 7L) & -8L;
+ }
+ memset(cqr, 0, sizeof(*cqr));
+ cqr->mem_chunk = chunk;
if (cplength > 0) {
- cqr->cpaddr = (struct ccw1 *) data;
- data += cplength*sizeof(struct ccw1);
- memset(cqr->cpaddr, 0, cplength*sizeof(struct ccw1));
+ cqr->cpaddr = data;
+ data += cplength * sizeof(struct ccw1);
+ memset(cqr->cpaddr, 0, cplength * sizeof(struct ccw1));
}
- cqr->data = NULL;
if (datasize > 0) {
cqr->data = data;
memset(cqr->data, 0, datasize);
}
EXPORT_SYMBOL(dasd_smalloc_request);
-/*
- * Free memory of a channel program. This function needs to free all the
- * idal lists that might have been created by dasd_set_cda and the
- * struct dasd_ccw_req itself.
- */
-void dasd_kfree_request(struct dasd_ccw_req *cqr, struct dasd_device *device)
-{
- struct ccw1 *ccw;
-
- /* Clear any idals used for the request. */
- ccw = cqr->cpaddr;
- do {
- clear_normalized_cda(ccw);
- } while (ccw++->flags & (CCW_FLAG_CC | CCW_FLAG_DC));
- kfree(cqr->cpaddr);
- kfree(cqr->data);
- kfree(cqr);
- dasd_put_device(device);
-}
-EXPORT_SYMBOL(dasd_kfree_request);
-
void dasd_sfree_request(struct dasd_ccw_req *cqr, struct dasd_device *device)
{
unsigned long flags;
spin_lock_irqsave(&device->mem_lock, flags);
- dasd_free_chunk(&device->ccw_chunks, cqr);
+ dasd_free_chunk(&device->ccw_chunks, cqr->mem_chunk);
spin_unlock_irqrestore(&device->mem_lock, flags);
dasd_put_device(device);
}
}
}
+static void __dasd_process_cqr(struct dasd_device *device,
+ struct dasd_ccw_req *cqr)
+{
+ char errorstring[ERRORLENGTH];
+
+ switch (cqr->status) {
+ case DASD_CQR_SUCCESS:
+ cqr->status = DASD_CQR_DONE;
+ break;
+ case DASD_CQR_ERROR:
+ cqr->status = DASD_CQR_NEED_ERP;
+ break;
+ case DASD_CQR_CLEARED:
+ cqr->status = DASD_CQR_TERMINATED;
+ break;
+ default:
+ /* internal error 12 - wrong cqr status*/
+ snprintf(errorstring, ERRORLENGTH, "12 %p %x02", cqr, cqr->status);
+ dev_err(&device->cdev->dev,
+ "An error occurred in the DASD device driver, "
+ "reason=%s\n", errorstring);
+ BUG();
+ }
+ if (cqr->callback)
+ cqr->callback(cqr, cqr->callback_data);
+}
+
/*
* the cqrs from the final queue are returned to the upper layer
* by setting a dasd_block state and calling the callback function
struct list_head *l, *n;
struct dasd_ccw_req *cqr;
struct dasd_block *block;
- void (*callback)(struct dasd_ccw_req *, void *data);
- void *callback_data;
- char errorstring[ERRORLENGTH];
list_for_each_safe(l, n, final_queue) {
cqr = list_entry(l, struct dasd_ccw_req, devlist);
list_del_init(&cqr->devlist);
block = cqr->block;
- callback = cqr->callback;
- callback_data = cqr->callback_data;
- if (block)
+ if (!block) {
+ __dasd_process_cqr(device, cqr);
+ } else {
spin_lock_bh(&block->queue_lock);
- switch (cqr->status) {
- case DASD_CQR_SUCCESS:
- cqr->status = DASD_CQR_DONE;
- break;
- case DASD_CQR_ERROR:
- cqr->status = DASD_CQR_NEED_ERP;
- break;
- case DASD_CQR_CLEARED:
- cqr->status = DASD_CQR_TERMINATED;
- break;
- default:
- /* internal error 12 - wrong cqr status*/
- snprintf(errorstring, ERRORLENGTH, "12 %p %x02", cqr, cqr->status);
- dev_err(&device->cdev->dev,
- "An error occurred in the DASD device driver, "
- "reason=%s\n", errorstring);
- BUG();
- }
- if (cqr->callback != NULL)
- (callback)(cqr, callback_data);
- if (block)
+ __dasd_process_cqr(device, cqr);
spin_unlock_bh(&block->queue_lock);
+ }
}
}
cqr->callback_data = req;
cqr->status = DASD_CQR_FILLED;
cqr->dq = dq;
- *((struct dasd_ccw_req **) blk_mq_rq_to_pdu(req)) = cqr;
blk_mq_start_request(req);
spin_lock(&block->queue_lock);
unsigned long flags;
int rc = 0;
- cqr = *((struct dasd_ccw_req **) blk_mq_rq_to_pdu(req));
+ cqr = blk_mq_rq_to_pdu(req);
if (!cqr)
return BLK_EH_DONE;
int rc;
block->tag_set.ops = &dasd_mq_ops;
- block->tag_set.cmd_size = sizeof(struct dasd_ccw_req *);
+ block->tag_set.cmd_size = sizeof(struct dasd_ccw_req);
block->tag_set.nr_hw_queues = DASD_NR_HW_QUEUES;
block->tag_set.queue_depth = DASD_MAX_LCU_DEV * DASD_REQ_PER_DEV;
block->tag_set.flags = BLK_MQ_F_SHOULD_MERGE;
struct ccw1 *ccw;
unsigned long *idaw;
- cqr = dasd_smalloc_request(magic, 1 /* RDC */, rdc_buffer_size, device);
+ cqr = dasd_smalloc_request(magic, 1 /* RDC */, rdc_buffer_size, device,
+ NULL);
if (IS_ERR(cqr)) {
/* internal error 13 - Allocating the RDC request failed*/
int rc;
unsigned long flags;
- cqr = dasd_kmalloc_request(DASD_ECKD_MAGIC, 1 /* PSF */ + 1 /* RSSD */,
+ cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* PSF */ + 1 /* RSSD */,
(sizeof(struct dasd_psf_prssd_data)),
- device);
+ device, NULL);
if (IS_ERR(cqr))
return PTR_ERR(cqr);
cqr->startdev = device;
lcu->flags |= NEED_UAC_UPDATE;
spin_unlock_irqrestore(&lcu->lock, flags);
}
- dasd_kfree_request(cqr, cqr->memdev);
+ dasd_sfree_request(cqr, cqr->memdev);
return rc;
}
/* Build the request */
datasize = sizeof(struct dasd_diag_req) +
count*sizeof(struct dasd_diag_bio);
- cqr = dasd_smalloc_request(DASD_DIAG_MAGIC, 0, datasize, memdev);
+ cqr = dasd_smalloc_request(DASD_DIAG_MAGIC, 0, datasize, memdev,
+ blk_mq_rq_to_pdu(req));
if (IS_ERR(cqr))
return cqr;
}
cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* RCD */,
0, /* use rcd_buf as data ara */
- device);
+ device, NULL);
if (IS_ERR(cqr)) {
DBF_DEV_EVENT(DBF_WARNING, device, "%s",
"Could not allocate RCD request");
cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* PSF */ + 1 /* RSSD */,
(sizeof(struct dasd_psf_prssd_data) +
sizeof(struct dasd_rssd_features)),
- device);
+ device, NULL);
if (IS_ERR(cqr)) {
DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", "Could not "
"allocate initialization request");
cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* PSF */ ,
sizeof(struct dasd_psf_ssc_data),
- device);
+ device, NULL);
if (IS_ERR(cqr)) {
DBF_DEV_EVENT(DBF_WARNING, device, "%s",
cplength = 8;
datasize = sizeof(struct DE_eckd_data) + 2*sizeof(struct LO_eckd_data);
- cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, cplength, datasize, device);
+ cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, cplength, datasize, device,
+ NULL);
if (IS_ERR(cqr))
return cqr;
ccw = cqr->cpaddr;
*/
itcw_size = itcw_calc_size(0, count, 0);
- cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 0, itcw_size, startdev);
+ cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 0, itcw_size, startdev,
+ NULL);
if (IS_ERR(cqr))
return cqr;
cplength += count;
cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, cplength, datasize,
- startdev);
+ startdev, NULL);
if (IS_ERR(cqr))
return cqr;
}
/* Allocate the format ccw request. */
fcp = dasd_smalloc_request(DASD_ECKD_MAGIC, cplength,
- datasize, startdev);
+ datasize, startdev, NULL);
if (IS_ERR(fcp))
return fcp;
}
/* Allocate the ccw request. */
cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, cplength, datasize,
- startdev);
+ startdev, blk_mq_rq_to_pdu(req));
if (IS_ERR(cqr))
return cqr;
ccw = cqr->cpaddr;
/* Allocate the ccw request. */
cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, cplength, datasize,
- startdev);
+ startdev, blk_mq_rq_to_pdu(req));
if (IS_ERR(cqr))
return cqr;
ccw = cqr->cpaddr;
/* Allocate the ccw request. */
itcw_size = itcw_calc_size(0, ctidaw, 0);
- cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 0, itcw_size, startdev);
+ cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 0, itcw_size, startdev,
+ blk_mq_rq_to_pdu(req));
if (IS_ERR(cqr))
return cqr;
/* Allocate the ccw request. */
cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, cplength,
- datasize, startdev);
+ datasize, startdev, blk_mq_rq_to_pdu(req));
if (IS_ERR(cqr))
return cqr;
return -EACCES;
useglobal = 0;
- cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1, 32, device);
+ cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1, 32, device, NULL);
if (IS_ERR(cqr)) {
mutex_lock(&dasd_reserve_mutex);
useglobal = 1;
return -EACCES;
useglobal = 0;
- cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1, 32, device);
+ cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1, 32, device, NULL);
if (IS_ERR(cqr)) {
mutex_lock(&dasd_reserve_mutex);
useglobal = 1;
return -EACCES;
useglobal = 0;
- cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1, 32, device);
+ cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1, 32, device, NULL);
if (IS_ERR(cqr)) {
mutex_lock(&dasd_reserve_mutex);
useglobal = 1;
useglobal = 0;
cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1,
- sizeof(struct dasd_snid_data), device);
+ sizeof(struct dasd_snid_data), device,
+ NULL);
if (IS_ERR(cqr)) {
mutex_lock(&dasd_reserve_mutex);
useglobal = 1;
cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* PSF */ + 1 /* RSSD */,
(sizeof(struct dasd_psf_prssd_data) +
sizeof(struct dasd_rssd_perf_stats_t)),
- device);
+ device, NULL);
if (IS_ERR(cqr)) {
DBF_DEV_EVENT(DBF_WARNING, device, "%s",
"Could not allocate initialization request");
psf1 = psf_data[1];
/* setup CCWs for PSF + RSSD */
- cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 2 , 0, device);
+ cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 2, 0, device, NULL);
if (IS_ERR(cqr)) {
DBF_DEV_EVENT(DBF_WARNING, device, "%s",
"Could not allocate initialization request");
cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* PSF */ + 1 /* RSSD */,
(sizeof(struct dasd_psf_prssd_data) +
sizeof(struct dasd_rssd_messages)),
- device);
+ device, NULL);
if (IS_ERR(cqr)) {
DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s",
"Could not allocate read message buffer request");
cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* PSF */ + 1 /* RSSD */,
sizeof(struct dasd_psf_prssd_data) + 1,
- device);
+ device, NULL);
if (IS_ERR(cqr)) {
DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s",
"Could not allocate read message buffer request");
int rc;
cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* PSF */ ,
- sizeof(struct dasd_psf_cuir_response),
- device);
+ sizeof(struct dasd_psf_cuir_response),
+ device, NULL);
if (IS_ERR(cqr)) {
DBF_DEV_EVENT(DBF_WARNING, device, "%s",
* is a new ccw in device->eer_cqr. Free the "old"
* snss request now.
*/
- dasd_kfree_request(cqr, device);
+ dasd_sfree_request(cqr, device);
}
/*
if (rc)
goto out;
- cqr = dasd_kmalloc_request(DASD_ECKD_MAGIC, 1 /* SNSS */,
- SNSS_DATA_SIZE, device);
+ cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* SNSS */,
+ SNSS_DATA_SIZE, device, NULL);
if (IS_ERR(cqr)) {
rc = -ENOMEM;
cqr = NULL;
spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
if (cqr)
- dasd_kfree_request(cqr, device);
+ dasd_sfree_request(cqr, device);
return rc;
}
in_use = test_and_clear_bit(DASD_FLAG_EER_IN_USE, &device->flags);
spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
if (cqr && !in_use)
- dasd_kfree_request(cqr, device);
+ dasd_sfree_request(cqr, device);
}
/*
datasize = sizeof(struct DE_fba_data) +
nr_ccws * (sizeof(struct LO_fba_data) + sizeof(struct ccw1));
- cqr = dasd_smalloc_request(DASD_FBA_MAGIC, cplength, datasize, memdev);
+ cqr = dasd_smalloc_request(DASD_FBA_MAGIC, cplength, datasize, memdev,
+ blk_mq_rq_to_pdu(req));
if (IS_ERR(cqr))
return cqr;
datasize += (count - 1)*sizeof(struct LO_fba_data);
}
/* Allocate the ccw request. */
- cqr = dasd_smalloc_request(DASD_FBA_MAGIC, cplength, datasize, memdev);
+ cqr = dasd_smalloc_request(DASD_FBA_MAGIC, cplength, datasize, memdev,
+ blk_mq_rq_to_pdu(req));
if (IS_ERR(cqr))
return cqr;
ccw = cqr->cpaddr;
struct dasd_ccw_req {
unsigned int magic; /* Eye catcher */
+ int intrc; /* internal error, e.g. from start_IO */
struct list_head devlist; /* for dasd_device request queue */
struct list_head blocklist; /* for dasd_block request queue */
-
- /* Where to execute what... */
struct dasd_block *block; /* the originating block device */
struct dasd_device *memdev; /* the device used to allocate this */
struct dasd_device *startdev; /* device the request is started on */
struct dasd_device *basedev; /* base device if no block->base */
void *cpaddr; /* address of ccw or tcw */
+ short retries; /* A retry counter */
unsigned char cpmode; /* 0 = cmd mode, 1 = itcw */
char status; /* status of this request */
- short retries; /* A retry counter */
+ char lpm; /* logical path mask */
unsigned long flags; /* flags of this request */
struct dasd_queue *dq;
-
- /* ... and how */
unsigned long starttime; /* jiffies time of request start */
unsigned long expires; /* expiration period in jiffies */
- char lpm; /* logical path mask */
void *data; /* pointer to data area */
-
- /* these are important for recovering erroneous requests */
- int intrc; /* internal error, e.g. from start_IO */
struct irb irb; /* device status in case of an error */
struct dasd_ccw_req *refers; /* ERP-chain queueing. */
void *function; /* originating ERP action */
+ void *mem_chunk;
- /* these are for statistics only */
unsigned long buildclk; /* TOD-clock of request generation */
unsigned long startclk; /* TOD-clock of request start */
unsigned long stopclk; /* TOD-clock of request interrupt */
unsigned long endclk; /* TOD-clock of request termination */
- /* Callback that is called after reaching final status. */
void (*callback)(struct dasd_ccw_req *, void *data);
void *callback_data;
};
extern struct kmem_cache *dasd_page_cache;
struct dasd_ccw_req *
-dasd_kmalloc_request(int , int, int, struct dasd_device *);
-struct dasd_ccw_req *
-dasd_smalloc_request(int , int, int, struct dasd_device *);
-void dasd_kfree_request(struct dasd_ccw_req *, struct dasd_device *);
+dasd_smalloc_request(int, int, int, struct dasd_device *, struct dasd_ccw_req *);
void dasd_sfree_request(struct dasd_ccw_req *, struct dasd_device *);
void dasd_wakeup_cb(struct dasd_ccw_req *, void *);
-static inline int
-dasd_kmalloc_set_cda(struct ccw1 *ccw, void *cda, struct dasd_device *device)
-{
- return set_normalized_cda(ccw, cda);
-}
-
struct dasd_device *dasd_alloc_device(void);
void dasd_free_device(struct dasd_device *);
# The following is required for define_trace.h to find ./trace.h
CFLAGS_trace.o := -I$(src)
+CFLAGS_vfio_ccw_fsm.o := -I$(src)
obj-y += airq.o blacklist.o chsc.o cio.o css.o chp.o idset.o isc.o \
fcx.o itcw.o crw.o ccwreq.o trace.o ioasm.o
#define CCWCHAIN_LEN_MAX 256
struct pfn_array {
+ /* Starting guest physical I/O address. */
unsigned long pa_iova;
+ /* Array that stores PFNs of the pages need to pin. */
unsigned long *pa_iova_pfn;
+ /* Array that receives PFNs of the pages pinned. */
unsigned long *pa_pfn;
+ /* Number of pages pinned from @pa_iova. */
int pa_nr;
};
};
/*
- * pfn_array_pin() - pin user pages in memory
+ * pfn_array_alloc_pin() - alloc memory for PFNs, then pin user pages in memory
* @pa: pfn_array on which to perform the operation
* @mdev: the mediated device to perform pin/unpin operations
+ * @iova: target guest physical address
+ * @len: number of bytes that should be pinned from @iova
*
- * Attempt to pin user pages in memory.
+ * Attempt to allocate memory for PFNs, and pin user pages in memory.
*
* Usage of pfn_array:
- * @pa->pa_iova starting guest physical I/O address. Assigned by caller.
- * @pa->pa_iova_pfn array that stores PFNs of the pages need to pin. Allocated
- * by caller.
- * @pa->pa_pfn array that receives PFNs of the pages pinned. Allocated by
- * caller.
- * @pa->pa_nr number of pages from @pa->pa_iova to pin. Assigned by
- * caller.
- * number of pages pinned. Assigned by callee.
+ * We expect (pa_nr == 0) and (pa_iova_pfn == NULL), any field in
+ * this structure will be filled in by this function.
*
* Returns:
* Number of pages pinned on success.
- * If @pa->pa_nr is 0 or negative, returns 0.
+ * If @pa->pa_nr is not 0, or @pa->pa_iova_pfn is not NULL initially,
+ * returns -EINVAL.
* If no pages were pinned, returns -errno.
*/
-static int pfn_array_pin(struct pfn_array *pa, struct device *mdev)
-{
- int i, ret;
-
- if (pa->pa_nr <= 0) {
- pa->pa_nr = 0;
- return 0;
- }
-
- pa->pa_iova_pfn[0] = pa->pa_iova >> PAGE_SHIFT;
- for (i = 1; i < pa->pa_nr; i++)
- pa->pa_iova_pfn[i] = pa->pa_iova_pfn[i - 1] + 1;
-
- ret = vfio_pin_pages(mdev, pa->pa_iova_pfn, pa->pa_nr,
- IOMMU_READ | IOMMU_WRITE, pa->pa_pfn);
-
- if (ret > 0 && ret != pa->pa_nr) {
- vfio_unpin_pages(mdev, pa->pa_iova_pfn, ret);
- pa->pa_nr = 0;
- return 0;
- }
-
- return ret;
-}
-
-/* Unpin the pages before releasing the memory. */
-static void pfn_array_unpin_free(struct pfn_array *pa, struct device *mdev)
-{
- vfio_unpin_pages(mdev, pa->pa_iova_pfn, pa->pa_nr);
- pa->pa_nr = 0;
- kfree(pa->pa_iova_pfn);
-}
-
-/* Alloc memory for PFNs, then pin pages with them. */
static int pfn_array_alloc_pin(struct pfn_array *pa, struct device *mdev,
u64 iova, unsigned int len)
{
- int ret = 0;
+ int i, ret = 0;
if (!len)
return 0;
- if (pa->pa_nr)
+ if (pa->pa_nr || pa->pa_iova_pfn)
return -EINVAL;
pa->pa_iova = iova;
return -ENOMEM;
pa->pa_pfn = pa->pa_iova_pfn + pa->pa_nr;
- ret = pfn_array_pin(pa, mdev);
+ pa->pa_iova_pfn[0] = pa->pa_iova >> PAGE_SHIFT;
+ for (i = 1; i < pa->pa_nr; i++)
+ pa->pa_iova_pfn[i] = pa->pa_iova_pfn[i - 1] + 1;
- if (ret > 0)
- return ret;
- else if (!ret)
+ ret = vfio_pin_pages(mdev, pa->pa_iova_pfn, pa->pa_nr,
+ IOMMU_READ | IOMMU_WRITE, pa->pa_pfn);
+
+ if (ret < 0) {
+ goto err_out;
+ } else if (ret > 0 && ret != pa->pa_nr) {
+ vfio_unpin_pages(mdev, pa->pa_iova_pfn, ret);
ret = -EINVAL;
+ goto err_out;
+ }
+ return ret;
+
+err_out:
+ pa->pa_nr = 0;
kfree(pa->pa_iova_pfn);
+ pa->pa_iova_pfn = NULL;
return ret;
}
+/* Unpin the pages before releasing the memory. */
+static void pfn_array_unpin_free(struct pfn_array *pa, struct device *mdev)
+{
+ vfio_unpin_pages(mdev, pa->pa_iova_pfn, pa->pa_nr);
+ pa->pa_nr = 0;
+ kfree(pa->pa_iova_pfn);
+}
+
static int pfn_array_table_init(struct pfn_array_table *pat, int nr)
{
pat->pat_pa = kcalloc(nr, sizeof(*pat->pat_pa), GFP_KERNEL);
* This is the chain length not considering any TICs.
* You need to do a new round for each TIC target.
*
+ * The program is also validated for absence of not yet supported
+ * indirect data addressing scenarios.
+ *
* Returns: the length of the ccw chain or -errno.
*/
static int ccwchain_calc_length(u64 iova, struct channel_program *cp)
do {
cnt++;
+ /*
+ * As we don't want to fail direct addressing even if the
+ * orb specified one of the unsupported formats, we defer
+ * checking for IDAWs in unsupported formats to here.
+ */
+ if ((!cp->orb.cmd.c64 || cp->orb.cmd.i2k) && ccw_is_idal(ccw))
+ return -EOPNOTSUPP;
+
if ((!ccw_is_chain(ccw)) && (!ccw_is_tic(ccw)))
break;
struct ccw1 *ccw;
struct pfn_array_table *pat;
unsigned long *idaws;
- int idaw_nr;
+ int ret;
ccw = chain->ch_ccw + idx;
* needed when translating a direct ccw to a idal ccw.
*/
pat = chain->ch_pat + idx;
- if (pfn_array_table_init(pat, 1))
- return -ENOMEM;
- idaw_nr = pfn_array_alloc_pin(pat->pat_pa, cp->mdev,
- ccw->cda, ccw->count);
- if (idaw_nr < 0)
- return idaw_nr;
+ ret = pfn_array_table_init(pat, 1);
+ if (ret)
+ goto out_init;
+
+ ret = pfn_array_alloc_pin(pat->pat_pa, cp->mdev, ccw->cda, ccw->count);
+ if (ret < 0)
+ goto out_init;
/* Translate this direct ccw to a idal ccw. */
- idaws = kcalloc(idaw_nr, sizeof(*idaws), GFP_DMA | GFP_KERNEL);
+ idaws = kcalloc(ret, sizeof(*idaws), GFP_DMA | GFP_KERNEL);
if (!idaws) {
- pfn_array_table_unpin_free(pat, cp->mdev);
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto out_unpin;
}
ccw->cda = (__u32) virt_to_phys(idaws);
ccw->flags |= CCW_FLAG_IDA;
pfn_array_table_idal_create_words(pat, idaws);
return 0;
+
+out_unpin:
+ pfn_array_table_unpin_free(pat, cp->mdev);
+out_init:
+ ccw->cda = 0;
+ return ret;
}
static int ccwchain_fetch_idal(struct ccwchain *chain,
pat = chain->ch_pat + idx;
ret = pfn_array_table_init(pat, idaw_nr);
if (ret)
- return ret;
+ goto out_init;
/* Translate idal ccw to use new allocated idaws. */
idaws = kzalloc(idaw_len, GFP_DMA | GFP_KERNEL);
kfree(idaws);
out_unpin:
pfn_array_table_unpin_free(pat, cp->mdev);
+out_init:
+ ccw->cda = 0;
return ret;
}
/*
* XXX:
* Only support prefetch enable mode now.
- * Only support 64bit addressing idal.
- * Only support 4k IDAW.
*/
- if (!orb->cmd.pfch || !orb->cmd.c64 || orb->cmd.i2k)
+ if (!orb->cmd.pfch)
return -EOPNOTSUPP;
INIT_LIST_HEAD(&cp->ccwchain_list);
ret = ccwchain_loop_tic(chain, cp);
if (ret)
cp_unpin_free(cp);
+ /* It is safe to force: if not set but idals used
+ * ccwchain_calc_length returns an error.
+ */
+ cp->orb.cmd.c64 = 1;
return ret;
}
{
struct vfio_ccw_private *private = dev_get_drvdata(&sch->dev);
unsigned long flags;
+ int rc = -EAGAIN;
spin_lock_irqsave(sch->lock, flags);
if (!device_is_registered(&sch->dev))
if (cio_update_schib(sch)) {
vfio_ccw_fsm_event(private, VFIO_CCW_EVENT_NOT_OPER);
+ rc = 0;
goto out_unlock;
}
private->state = private->mdev ? VFIO_CCW_STATE_IDLE :
VFIO_CCW_STATE_STANDBY;
}
+ rc = 0;
out_unlock:
spin_unlock_irqrestore(sch->lock, flags);
- return 0;
+ return rc;
}
static struct css_device_id vfio_ccw_sch_ids[] = {
#include "ioasm.h"
#include "vfio_ccw_private.h"
+#define CREATE_TRACE_POINTS
+#include "vfio_ccw_trace.h"
+
static int fsm_io_helper(struct vfio_ccw_private *private)
{
struct subchannel *sch;
*/
cio_disable_subchannel(sch);
}
+inline struct subchannel_id get_schid(struct vfio_ccw_private *p)
+{
+ return p->sch->schid;
+}
/*
* Deal with the ccw command request from the userspace.
union scsw *scsw = &private->scsw;
struct ccw_io_region *io_region = &private->io_region;
struct mdev_device *mdev = private->mdev;
+ char *errstr = "request";
private->state = VFIO_CCW_STATE_BOXED;
/* Don't try to build a cp if transport mode is specified. */
if (orb->tm.b) {
io_region->ret_code = -EOPNOTSUPP;
+ errstr = "transport mode";
goto err_out;
}
io_region->ret_code = cp_init(&private->cp, mdev_dev(mdev),
orb);
- if (io_region->ret_code)
+ if (io_region->ret_code) {
+ errstr = "cp init";
goto err_out;
+ }
io_region->ret_code = cp_prefetch(&private->cp);
if (io_region->ret_code) {
+ errstr = "cp prefetch";
cp_free(&private->cp);
goto err_out;
}
/* Start channel program and wait for I/O interrupt. */
io_region->ret_code = fsm_io_helper(private);
if (io_region->ret_code) {
+ errstr = "cp fsm_io_helper";
cp_free(&private->cp);
goto err_out;
}
err_out:
private->state = VFIO_CCW_STATE_IDLE;
+ trace_vfio_ccw_io_fctl(scsw->cmd.fctl, get_schid(private),
+ io_region->ret_code, errstr);
}
/*
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0
+ * Tracepoints for vfio_ccw driver
+ *
+ * Copyright IBM Corp. 2018
+ *
+ * Author(s): Dong Jia Shi <bjsdjshi@linux.vnet.ibm.com>
+ * Halil Pasic <pasic@linux.vnet.ibm.com>
+ */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM vfio_ccw
+
+#if !defined(_VFIO_CCW_TRACE_) || defined(TRACE_HEADER_MULTI_READ)
+#define _VFIO_CCW_TRACE_
+
+#include <linux/tracepoint.h>
+
+TRACE_EVENT(vfio_ccw_io_fctl,
+ TP_PROTO(int fctl, struct subchannel_id schid, int errno, char *errstr),
+ TP_ARGS(fctl, schid, errno, errstr),
+
+ TP_STRUCT__entry(
+ __field(int, fctl)
+ __field_struct(struct subchannel_id, schid)
+ __field(int, errno)
+ __field(char*, errstr)
+ ),
+
+ TP_fast_assign(
+ __entry->fctl = fctl;
+ __entry->schid = schid;
+ __entry->errno = errno;
+ __entry->errstr = errstr;
+ ),
+
+ TP_printk("schid=%x.%x.%04x fctl=%x errno=%d info=%s",
+ __entry->schid.cssid,
+ __entry->schid.ssid,
+ __entry->schid.sch_no,
+ __entry->fctl,
+ __entry->errno,
+ __entry->errstr)
+);
+
+#endif /* _VFIO_CCW_TRACE_ */
+
+/* This part must be outside protection */
+
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE vfio_ccw_trace
+
+#include <trace/define_trace.h>
* the recommended way for applications to use the coprocessor, and
* the driver interface is not intended for general use.
*
- * See Documentation/sparc/oradax/oracle_dax.txt for more details.
+ * See Documentation/sparc/oradax/oracle-dax.txt for more details.
*/
#include <linux/uaccess.h>
rcode = -EINVAL;
goto cleanup;
}
- p = kmalloc(sg_count[i], GFP_KERNEL|GFP_DMA32);
+ p = kmalloc(sg_count[i], GFP_KERNEL);
if (!p) {
dprintk((KERN_DEBUG"aacraid: Could not allocate SG buffer - size = %d buffer number %d of %d\n",
sg_count[i], i, usg->count));
rcode = -EINVAL;
goto cleanup;
}
- p = kmalloc(sg_count[i], GFP_KERNEL|GFP_DMA32);
+ p = kmalloc(sg_count[i], GFP_KERNEL);
if (!p) {
dprintk((KERN_DEBUG"aacraid: Could not allocate SG buffer - size = %d buffer number %d of %d\n",
sg_count[i], i, upsg->count));
ahd->int_coalescing_stop_threshold =
AHD_INT_COALESCING_STOP_THRESHOLD_DEFAULT;
- if (ahd_platform_alloc(ahd, platform_arg) != 0) {
- ahd_free(ahd);
- ahd = NULL;
- }
#ifdef AHD_DEBUG
if ((ahd_debug & AHD_SHOW_MEMORY) != 0) {
printk("%s: scb size = 0x%x, hscb size = 0x%x\n",
(u_int)sizeof(struct hardware_scb));
}
#endif
+ if (ahd_platform_alloc(ahd, platform_arg) != 0) {
+ ahd_free(ahd);
+ ahd = NULL;
+ }
return (ahd);
}
kfree(options);
}
-static void hpsa_shutdown(struct pci_dev *pdev)
+static void __hpsa_shutdown(struct pci_dev *pdev)
{
struct ctlr_info *h;
hpsa_disable_interrupt_mode(h); /* pci_init 2 */
}
+static void hpsa_shutdown(struct pci_dev *pdev)
+{
+ __hpsa_shutdown(pdev);
+ pci_disable_device(pdev);
+}
+
static void hpsa_free_device_info(struct ctlr_info *h)
{
int i;
scsi_remove_host(h->scsi_host); /* init_one 8 */
/* includes hpsa_free_irqs - init_one 4 */
/* includes hpsa_disable_interrupt_mode - pci_init 2 */
- hpsa_shutdown(pdev);
+ __hpsa_shutdown(pdev);
hpsa_free_device_info(h); /* scan */
spin_lock_irqsave(writeq_lock, flags);
writel((u32)(data_out), addr);
writel((u32)(data_out >> 32), (addr + 4));
+ mmiowb();
spin_unlock_irqrestore(writeq_lock, flags);
}
return;
if (fcport->fp_speed == PORT_SPEED_UNKNOWN ||
- fcport->fp_speed > ha->link_data_rate)
+ fcport->fp_speed > ha->link_data_rate ||
+ !ha->flags.gpsc_supported)
return;
rval = qla2x00_set_idma_speed(vha, fcport->loop_id, fcport->fp_speed,
ox_id = le16_to_cpu(sts24->ox_id);
par_sense_len = sizeof(sts24->data);
/* Valid values of the retry delay timer are 0x1-0xffef */
- if (sts24->retry_delay > 0 && sts24->retry_delay < 0xfff1)
- retry_delay = sts24->retry_delay;
+ if (sts24->retry_delay > 0 && sts24->retry_delay < 0xfff1) {
+ retry_delay = sts24->retry_delay & 0x3fff;
+ ql_dbg(ql_dbg_io, sp->vha, 0x3033,
+ "%s: scope=%#x retry_delay=%#x\n", __func__,
+ sts24->retry_delay >> 14, retry_delay);
+ }
} else {
if (scsi_status & SS_SENSE_LEN_VALID)
sense_len = le16_to_cpu(sts->req_sense_length);
if (time_after(jiffies, wait_time))
break;
+ /*
+ * Check if it's UNLOADING, cause we cannot poll in
+ * this case, or else a NULL pointer dereference
+ * is triggered.
+ */
+ if (unlikely(test_bit(UNLOADING, &base_vha->dpc_flags)))
+ return QLA_FUNCTION_TIMEOUT;
+
/* Check for pending interrupts. */
qla2x00_poll(ha->rsp_q_map[0]);
} else if (this_zone_blocks != zone_blocks &&
(block + this_zone_blocks < sdkp->capacity
|| this_zone_blocks > zone_blocks)) {
- this_zone_blocks = 0;
+ zone_blocks = 0;
goto out;
}
block += this_zone_blocks;
const struct elf32_phdr *phdrs;
const struct elf32_phdr *phdr;
const struct elf32_hdr *ehdr;
- phys_addr_t min_addr = (phys_addr_t)ULLONG_MAX;
+ phys_addr_t min_addr = PHYS_ADDR_MAX;
phys_addr_t max_addr = 0;
int i;
const struct elf32_hdr *ehdr;
const struct firmware *seg_fw;
phys_addr_t mem_reloc;
- phys_addr_t min_addr = (phys_addr_t)ULLONG_MAX;
+ phys_addr_t min_addr = PHYS_ADDR_MAX;
phys_addr_t max_addr = 0;
size_t fw_name_len;
ssize_t offset;
*
* @stream: Soundwire stream
*
- * Documentation/soundwire/stream.txt explains this API in detail
+ * Documentation/driver-api/soundwire/stream.rst explains this API in detail
*/
int sdw_prepare_stream(struct sdw_stream_runtime *stream)
{
*
* @stream: Soundwire stream
*
- * Documentation/soundwire/stream.txt explains this API in detail
+ * Documentation/driver-api/soundwire/stream.rst explains this API in detail
*/
int sdw_enable_stream(struct sdw_stream_runtime *stream)
{
*
* @stream: Soundwire stream
*
- * Documentation/soundwire/stream.txt explains this API in detail
+ * Documentation/driver-api/soundwire/stream.rst explains this API in detail
*/
int sdw_disable_stream(struct sdw_stream_runtime *stream)
{
*
* @stream: Soundwire stream
*
- * Documentation/soundwire/stream.txt explains this API in detail
+ * Documentation/driver-api/soundwire/stream.rst explains this API in detail
*/
int sdw_deprepare_stream(struct sdw_stream_runtime *stream)
{
This document provides an overview the Linux DPIO driver, its
subcomponents, and its APIs.
-See Documentation/dpaa2/overview.txt for a general overview of DPAA2
+See Documentation/networking/dpaa2/overview.rst for a general overview of DPAA2
and the general DPAA2 driver architecture in Linux.
Driver Overview
From the initial code review:
The main thing you need to do is to implement all the controls using the
-control framework (see Documentation/video4linux/v4l2-controls.txt).
+control framework (see Documentation/media/kapi/v4l2-controls.rst).
Most drivers are by now converted to the control framework, so you will
find many examples of how to do this in drivers/media/radio.
36057/36067 PCI controller chipset. This includes the Iomega
Buz, Pinnacle DC10+ and the Linux Media Labs LML33. There is
a driver homepage at <http://mjpeg.sf.net/driver-zoran/>. For
- more information, check <file:Documentation/video4linux/Zoran>.
+ more information, check <file:Documentation/media/v4l-drivers/zoran.rst>.
To compile this driver as a module, choose M here: the
module will be called zr36067.
i = -EIO;
tty_ldisc_deref(ld);
- if (i > 0)
- tty_update_time(&inode->i_atime);
+ if (i > 0) {
+ struct timespec ts;
+
+ ts = timespec64_to_timespec(inode->i_atime);
+ tty_update_time(&ts);
+ inode->i_atime = timespec_to_timespec64(ts);
+ }
return i;
}
cond_resched();
}
if (written) {
- tty_update_time(&file_inode(file)->i_mtime);
+ struct timespec ts;
+
+ ts = timespec64_to_timespec(file_inode(file)->i_mtime);
+ tty_update_time(&ts);
+ file_inode(file)->i_mtime = timespec_to_timespec64(ts);
ret = written;
}
out:
inode = new_inode(sb);
if (likely(inode)) {
- struct timespec ts = current_time(inode);
+ struct timespec64 ts = current_time(inode);
inode->i_ino = get_next_ino();
inode->i_mode = perms->mode;
struct vhost_msg_node *node = kmalloc(sizeof *node, GFP_KERNEL);
if (!node)
return NULL;
+
+ /* Make sure all padding within the structure is initialized. */
+ memset(&node->msg, 0, sizeof node->msg);
node->vq = vq;
node->msg.type = type;
return node;
config FB_VIA
tristate "VIA UniChrome (Pro) and Chrome9 display support"
- depends on FB && PCI && X86 && GPIOLIB && I2C
+ depends on FB && PCI && GPIOLIB && I2C && (X86 || COMPILE_TEST)
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT
config FB_SH_MOBILE_LCDC
tristate "SuperH Mobile LCDC framebuffer support"
depends on FB && (SUPERH || ARCH_RENESAS) && HAVE_CLK
- depends on FB_SH_MOBILE_MERAM || !FB_SH_MOBILE_MERAM
select FB_SYS_FILLRECT
select FB_SYS_COPYAREA
select FB_SYS_IMAGEBLIT
and could also have been called by other names when coupled with
a bridge adapter.
-config FB_AUO_K190X
- tristate "AUO-K190X EPD controller support"
- depends on FB
- select FB_SYS_FILLRECT
- select FB_SYS_COPYAREA
- select FB_SYS_IMAGEBLIT
- select FB_SYS_FOPS
- select FB_DEFERRED_IO
- help
- Provides support for epaper controllers from the K190X series
- of AUO. These controllers can be used to drive epaper displays
- from Sipix.
-
- This option enables the common support, shared by the individual
- controller drivers. You will also have to enable the driver
- for the controller type used in your device.
-
-config FB_AUO_K1900
- tristate "AUO-K1900 EPD controller support"
- depends on FB && FB_AUO_K190X
- help
- This driver implements support for the AUO K1900 epd-controller.
- This controller can drive Sipix epaper displays but can only do
- serial updates, reducing the number of possible frames per second.
-
-config FB_AUO_K1901
- tristate "AUO-K1901 EPD controller support"
- depends on FB && FB_AUO_K190X
- help
- This driver implements support for the AUO K1901 epd-controller.
- This controller can drive Sipix epaper displays and supports
- concurrent updates, making higher frames per second possible.
-
config FB_JZ4740
tristate "JZ4740 LCD framebuffer support"
depends on FB && MACH_JZ4740
source "drivers/video/fbdev/omap2/Kconfig"
source "drivers/video/fbdev/mmp/Kconfig"
-config FB_SH_MOBILE_MERAM
- tristate "SuperH Mobile MERAM read ahead support"
- depends on (SUPERH || ARCH_SHMOBILE)
- select GENERIC_ALLOCATOR
- ---help---
- Enable MERAM support for the SuperH controller.
-
- This will allow for caching of the framebuffer to provide more
- reliable access under heavy main memory bus traffic situations.
- Up to 4 memory channels can be configured, allowing 4 RGB or
- 2 YCbCr framebuffers to be configured.
-
config FB_SSD1307
tristate "Solomon SSD1307 framebuffer support"
depends on FB && I2C
obj-$(CONFIG_FB_MAXINE) += maxinefb.o
obj-$(CONFIG_FB_METRONOME) += metronomefb.o
obj-$(CONFIG_FB_BROADSHEET) += broadsheetfb.o
-obj-$(CONFIG_FB_AUO_K190X) += auo_k190x.o
-obj-$(CONFIG_FB_AUO_K1900) += auo_k1900fb.o
-obj-$(CONFIG_FB_AUO_K1901) += auo_k1901fb.o
obj-$(CONFIG_FB_S1D13XXX) += s1d13xxxfb.o
obj-$(CONFIG_FB_SH7760) += sh7760fb.o
obj-$(CONFIG_FB_IMX) += imxfb.o
obj-$(CONFIG_FB_UDL) += udlfb.o
obj-$(CONFIG_FB_SMSCUFX) += smscufx.o
obj-$(CONFIG_FB_XILINX) += xilinxfb.o
-obj-$(CONFIG_FB_SH_MOBILE_MERAM) += sh_mobile_meram.o
obj-$(CONFIG_FB_SH_MOBILE_LCDC) += sh_mobile_lcdcfb.o
obj-$(CONFIG_FB_OMAP) += omap/
obj-y += omap2/
(void)aty_ld_pll(POWER_MANAGEMENT);
aty_st_le32(BUS_CNTL1, 0x00000010);
aty_st_le32(MEM_POWER_MISC, 0x0c830000);
- mdelay(100);
+ msleep(100);
/* Switch PCI power management to D2 */
pci_set_power_state(pdev, PCI_D2);
* it, we'll restore the dynamic clocks state on wakeup
*/
radeon_pm_disable_dynamic_mode(rinfo);
- mdelay(50);
+ msleep(50);
radeon_pm_save_regs(rinfo, 1);
if (rinfo->is_mobility && !(rinfo->pm_mode & radeon_pm_d2)) {
/* Switch off LVDS interface */
- mdelay(1);
+ usleep_range(1000, 2000);
OUTREG(LVDS_GEN_CNTL, INREG(LVDS_GEN_CNTL) & ~(LVDS_BL_MOD_EN));
- mdelay(1);
+ usleep_range(1000, 2000);
OUTREG(LVDS_GEN_CNTL, INREG(LVDS_GEN_CNTL) & ~(LVDS_EN | LVDS_ON));
OUTREG(LVDS_PLL_CNTL, (INREG(LVDS_PLL_CNTL) & ~30000) | 0x20000);
- mdelay(20);
+ msleep(20);
OUTREG(LVDS_GEN_CNTL, INREG(LVDS_GEN_CNTL) & ~(LVDS_DIGON));
}
pci_disable_device(pdev);
PAGE_ALIGN(fbdev->fb_len),
&fbdev->fb_phys, GFP_KERNEL);
if (!fbdev->fb_mem) {
- print_err("fail to allocate frambuffer (size: %dK))",
+ print_err("fail to allocate framebuffer (size: %dK))",
fbdev->fb_len / 1024);
return -ENOMEM;
}
&fbdev->fb_phys, GFP_KERNEL,
DMA_ATTR_NON_CONSISTENT);
if (!fbdev->fb_mem) {
- print_err("fail to allocate frambuffer (size: %dK))",
+ print_err("fail to allocate framebuffer (size: %dK))",
fbdev->fb_len / 1024);
ret = -ENOMEM;
goto failed;
+++ /dev/null
-/*
- * auok190xfb.c -- FB driver for AUO-K1900 controllers
- *
- * Copyright (C) 2011, 2012 Heiko Stuebner <heiko@sntech.de>
- *
- * based on broadsheetfb.c
- *
- * Copyright (C) 2008, Jaya Kumar
- *
- * 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.
- *
- * Layout is based on skeletonfb.c by James Simmons and Geert Uytterhoeven.
- *
- * This driver is written to be used with the AUO-K1900 display controller.
- *
- * It is intended to be architecture independent. A board specific driver
- * must be used to perform all the physical IO interactions.
- *
- * The controller supports different update modes:
- * mode0+1 16 step gray (4bit)
- * mode2 4 step gray (2bit) - FIXME: add strange refresh
- * mode3 2 step gray (1bit) - FIXME: add strange refresh
- * mode4 handwriting mode (strange behaviour)
- * mode5 automatic selection of update mode
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/string.h>
-#include <linux/mm.h>
-#include <linux/slab.h>
-#include <linux/delay.h>
-#include <linux/interrupt.h>
-#include <linux/fb.h>
-#include <linux/init.h>
-#include <linux/platform_device.h>
-#include <linux/list.h>
-#include <linux/firmware.h>
-#include <linux/gpio.h>
-#include <linux/pm_runtime.h>
-
-#include <video/auo_k190xfb.h>
-
-#include "auo_k190x.h"
-
-/*
- * AUO-K1900 specific commands
- */
-
-#define AUOK1900_CMD_PARTIALDISP 0x1001
-#define AUOK1900_CMD_ROTATION 0x1006
-#define AUOK1900_CMD_LUT_STOP 0x1009
-
-#define AUOK1900_INIT_TEMP_AVERAGE (1 << 13)
-#define AUOK1900_INIT_ROTATE(_x) ((_x & 0x3) << 10)
-#define AUOK1900_INIT_RESOLUTION(_res) ((_res & 0x7) << 2)
-
-static void auok1900_init(struct auok190xfb_par *par)
-{
- struct device *dev = par->info->device;
- struct auok190x_board *board = par->board;
- u16 init_param = 0;
-
- pm_runtime_get_sync(dev);
-
- init_param |= AUOK1900_INIT_TEMP_AVERAGE;
- init_param |= AUOK1900_INIT_ROTATE(par->rotation);
- init_param |= AUOK190X_INIT_INVERSE_WHITE;
- init_param |= AUOK190X_INIT_FORMAT0;
- init_param |= AUOK1900_INIT_RESOLUTION(par->resolution);
- init_param |= AUOK190X_INIT_SHIFT_RIGHT;
-
- auok190x_send_cmdargs(par, AUOK190X_CMD_INIT, 1, &init_param);
-
- /* let the controller finish */
- board->wait_for_rdy(par);
-
- pm_runtime_mark_last_busy(dev);
- pm_runtime_put_autosuspend(dev);
-}
-
-static void auok1900_update_region(struct auok190xfb_par *par, int mode,
- u16 y1, u16 y2)
-{
- struct device *dev = par->info->device;
- unsigned char *buf = (unsigned char *)par->info->screen_base;
- int xres = par->info->var.xres;
- int line_length = par->info->fix.line_length;
- u16 args[4];
-
- pm_runtime_get_sync(dev);
-
- mutex_lock(&(par->io_lock));
-
- /* y1 and y2 must be a multiple of 2 so drop the lowest bit */
- y1 &= 0xfffe;
- y2 &= 0xfffe;
-
- dev_dbg(dev, "update (x,y,w,h,mode)=(%d,%d,%d,%d,%d)\n",
- 1, y1+1, xres, y2-y1, mode);
-
- /* to FIX handle different partial update modes */
- args[0] = mode | 1;
- args[1] = y1 + 1;
- args[2] = xres;
- args[3] = y2 - y1;
- buf += y1 * line_length;
- auok190x_send_cmdargs_pixels(par, AUOK1900_CMD_PARTIALDISP, 4, args,
- ((y2 - y1) * line_length)/2, (u16 *) buf);
- auok190x_send_command(par, AUOK190X_CMD_DATA_STOP);
-
- par->update_cnt++;
-
- mutex_unlock(&(par->io_lock));
-
- pm_runtime_mark_last_busy(dev);
- pm_runtime_put_autosuspend(dev);
-}
-
-static void auok1900fb_dpy_update_pages(struct auok190xfb_par *par,
- u16 y1, u16 y2)
-{
- int mode;
-
- if (par->update_mode < 0) {
- mode = AUOK190X_UPDATE_MODE(1);
- par->last_mode = -1;
- } else {
- mode = AUOK190X_UPDATE_MODE(par->update_mode);
- par->last_mode = par->update_mode;
- }
-
- if (par->flash)
- mode |= AUOK190X_UPDATE_NONFLASH;
-
- auok1900_update_region(par, mode, y1, y2);
-}
-
-static void auok1900fb_dpy_update(struct auok190xfb_par *par)
-{
- int mode;
-
- if (par->update_mode < 0) {
- mode = AUOK190X_UPDATE_MODE(0);
- par->last_mode = -1;
- } else {
- mode = AUOK190X_UPDATE_MODE(par->update_mode);
- par->last_mode = par->update_mode;
- }
-
- if (par->flash)
- mode |= AUOK190X_UPDATE_NONFLASH;
-
- auok1900_update_region(par, mode, 0, par->info->var.yres);
- par->update_cnt = 0;
-}
-
-static bool auok1900fb_need_refresh(struct auok190xfb_par *par)
-{
- return (par->update_cnt > 10);
-}
-
-static int auok1900fb_probe(struct platform_device *pdev)
-{
- struct auok190x_init_data init;
- struct auok190x_board *board;
-
- /* pick up board specific routines */
- board = pdev->dev.platform_data;
- if (!board)
- return -EINVAL;
-
- /* fill temporary init struct for common init */
- init.id = "auo_k1900fb";
- init.board = board;
- init.update_partial = auok1900fb_dpy_update_pages;
- init.update_all = auok1900fb_dpy_update;
- init.need_refresh = auok1900fb_need_refresh;
- init.init = auok1900_init;
-
- return auok190x_common_probe(pdev, &init);
-}
-
-static int auok1900fb_remove(struct platform_device *pdev)
-{
- return auok190x_common_remove(pdev);
-}
-
-static struct platform_driver auok1900fb_driver = {
- .probe = auok1900fb_probe,
- .remove = auok1900fb_remove,
- .driver = {
- .name = "auo_k1900fb",
- .pm = &auok190x_pm,
- },
-};
-module_platform_driver(auok1900fb_driver);
-
-MODULE_DESCRIPTION("framebuffer driver for the AUO-K1900 EPD controller");
-MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>");
-MODULE_LICENSE("GPL");
+++ /dev/null
-/*
- * auok190xfb.c -- FB driver for AUO-K1901 controllers
- *
- * Copyright (C) 2011, 2012 Heiko Stuebner <heiko@sntech.de>
- *
- * based on broadsheetfb.c
- *
- * Copyright (C) 2008, Jaya Kumar
- *
- * 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.
- *
- * Layout is based on skeletonfb.c by James Simmons and Geert Uytterhoeven.
- *
- * This driver is written to be used with the AUO-K1901 display controller.
- *
- * It is intended to be architecture independent. A board specific driver
- * must be used to perform all the physical IO interactions.
- *
- * The controller supports different update modes:
- * mode0+1 16 step gray (4bit)
- * mode2+3 4 step gray (2bit)
- * mode4+5 2 step gray (1bit)
- * - mode4 is described as "without LUT"
- * mode7 automatic selection of update mode
- *
- * The most interesting difference to the K1900 is the ability to do screen
- * updates in an asynchronous fashion. Where the K1900 needs to wait for the
- * current update to complete, the K1901 can process later updates already.
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/string.h>
-#include <linux/mm.h>
-#include <linux/slab.h>
-#include <linux/delay.h>
-#include <linux/interrupt.h>
-#include <linux/fb.h>
-#include <linux/init.h>
-#include <linux/platform_device.h>
-#include <linux/list.h>
-#include <linux/firmware.h>
-#include <linux/gpio.h>
-#include <linux/pm_runtime.h>
-
-#include <video/auo_k190xfb.h>
-
-#include "auo_k190x.h"
-
-/*
- * AUO-K1901 specific commands
- */
-
-#define AUOK1901_CMD_LUT_INTERFACE 0x0005
-#define AUOK1901_CMD_DMA_START 0x1001
-#define AUOK1901_CMD_CURSOR_START 0x1007
-#define AUOK1901_CMD_CURSOR_STOP AUOK190X_CMD_DATA_STOP
-#define AUOK1901_CMD_DDMA_START 0x1009
-
-#define AUOK1901_INIT_GATE_PULSE_LOW (0 << 14)
-#define AUOK1901_INIT_GATE_PULSE_HIGH (1 << 14)
-#define AUOK1901_INIT_SINGLE_GATE (0 << 13)
-#define AUOK1901_INIT_DOUBLE_GATE (1 << 13)
-
-/* Bits to pixels
- * Mode 15-12 11-8 7-4 3-0
- * format2 2 T 1 T
- * format3 1 T 2 T
- * format4 T 2 T 1
- * format5 T 1 T 2
- *
- * halftone modes:
- * format6 2 2 1 1
- * format7 1 1 2 2
- */
-#define AUOK1901_INIT_FORMAT2 (1 << 7)
-#define AUOK1901_INIT_FORMAT3 ((1 << 7) | (1 << 6))
-#define AUOK1901_INIT_FORMAT4 (1 << 8)
-#define AUOK1901_INIT_FORMAT5 ((1 << 8) | (1 << 6))
-#define AUOK1901_INIT_FORMAT6 ((1 << 8) | (1 << 7))
-#define AUOK1901_INIT_FORMAT7 ((1 << 8) | (1 << 7) | (1 << 6))
-
-/* res[4] to bit 10
- * res[3-0] to bits 5-2
- */
-#define AUOK1901_INIT_RESOLUTION(_res) (((_res & (1 << 4)) << 6) \
- | ((_res & 0xf) << 2))
-
-/*
- * portrait / landscape orientation in AUOK1901_CMD_DMA_START
- */
-#define AUOK1901_DMA_ROTATE90(_rot) ((_rot & 1) << 13)
-
-/*
- * equivalent to 1 << 11, needs the ~ to have same rotation like K1900
- */
-#define AUOK1901_DDMA_ROTATE180(_rot) ((~_rot & 2) << 10)
-
-static void auok1901_init(struct auok190xfb_par *par)
-{
- struct device *dev = par->info->device;
- struct auok190x_board *board = par->board;
- u16 init_param = 0;
-
- pm_runtime_get_sync(dev);
-
- init_param |= AUOK190X_INIT_INVERSE_WHITE;
- init_param |= AUOK190X_INIT_FORMAT0;
- init_param |= AUOK1901_INIT_RESOLUTION(par->resolution);
- init_param |= AUOK190X_INIT_SHIFT_LEFT;
-
- auok190x_send_cmdargs(par, AUOK190X_CMD_INIT, 1, &init_param);
-
- /* let the controller finish */
- board->wait_for_rdy(par);
-
- pm_runtime_mark_last_busy(dev);
- pm_runtime_put_autosuspend(dev);
-}
-
-static void auok1901_update_region(struct auok190xfb_par *par, int mode,
- u16 y1, u16 y2)
-{
- struct device *dev = par->info->device;
- unsigned char *buf = (unsigned char *)par->info->screen_base;
- int xres = par->info->var.xres;
- int line_length = par->info->fix.line_length;
- u16 args[5];
-
- pm_runtime_get_sync(dev);
-
- mutex_lock(&(par->io_lock));
-
- /* y1 and y2 must be a multiple of 2 so drop the lowest bit */
- y1 &= 0xfffe;
- y2 &= 0xfffe;
-
- dev_dbg(dev, "update (x,y,w,h,mode)=(%d,%d,%d,%d,%d)\n",
- 1, y1+1, xres, y2-y1, mode);
-
- /* K1901: first transfer the region data */
- args[0] = AUOK1901_DMA_ROTATE90(par->rotation) | 1;
- args[1] = y1 + 1;
- args[2] = xres;
- args[3] = y2 - y1;
- buf += y1 * line_length;
- auok190x_send_cmdargs_pixels_nowait(par, AUOK1901_CMD_DMA_START, 4,
- args, ((y2 - y1) * line_length)/2,
- (u16 *) buf);
- auok190x_send_command_nowait(par, AUOK190X_CMD_DATA_STOP);
-
- /* K1901: second tell the controller to update the region with mode */
- args[0] = mode | AUOK1901_DDMA_ROTATE180(par->rotation);
- args[1] = 1;
- args[2] = y1 + 1;
- args[3] = xres;
- args[4] = y2 - y1;
- auok190x_send_cmdargs_nowait(par, AUOK1901_CMD_DDMA_START, 5, args);
-
- par->update_cnt++;
-
- mutex_unlock(&(par->io_lock));
-
- pm_runtime_mark_last_busy(dev);
- pm_runtime_put_autosuspend(dev);
-}
-
-static void auok1901fb_dpy_update_pages(struct auok190xfb_par *par,
- u16 y1, u16 y2)
-{
- int mode;
-
- if (par->update_mode < 0) {
- mode = AUOK190X_UPDATE_MODE(1);
- par->last_mode = -1;
- } else {
- mode = AUOK190X_UPDATE_MODE(par->update_mode);
- par->last_mode = par->update_mode;
- }
-
- if (par->flash)
- mode |= AUOK190X_UPDATE_NONFLASH;
-
- auok1901_update_region(par, mode, y1, y2);
-}
-
-static void auok1901fb_dpy_update(struct auok190xfb_par *par)
-{
- int mode;
-
- /* When doing full updates, wait for the controller to be ready
- * This will hopefully catch some hangs of the K1901
- */
- par->board->wait_for_rdy(par);
-
- if (par->update_mode < 0) {
- mode = AUOK190X_UPDATE_MODE(0);
- par->last_mode = -1;
- } else {
- mode = AUOK190X_UPDATE_MODE(par->update_mode);
- par->last_mode = par->update_mode;
- }
-
- if (par->flash)
- mode |= AUOK190X_UPDATE_NONFLASH;
-
- auok1901_update_region(par, mode, 0, par->info->var.yres);
- par->update_cnt = 0;
-}
-
-static bool auok1901fb_need_refresh(struct auok190xfb_par *par)
-{
- return (par->update_cnt > 10);
-}
-
-static int auok1901fb_probe(struct platform_device *pdev)
-{
- struct auok190x_init_data init;
- struct auok190x_board *board;
-
- /* pick up board specific routines */
- board = pdev->dev.platform_data;
- if (!board)
- return -EINVAL;
-
- /* fill temporary init struct for common init */
- init.id = "auo_k1901fb";
- init.board = board;
- init.update_partial = auok1901fb_dpy_update_pages;
- init.update_all = auok1901fb_dpy_update;
- init.need_refresh = auok1901fb_need_refresh;
- init.init = auok1901_init;
-
- return auok190x_common_probe(pdev, &init);
-}
-
-static int auok1901fb_remove(struct platform_device *pdev)
-{
- return auok190x_common_remove(pdev);
-}
-
-static struct platform_driver auok1901fb_driver = {
- .probe = auok1901fb_probe,
- .remove = auok1901fb_remove,
- .driver = {
- .name = "auo_k1901fb",
- .pm = &auok190x_pm,
- },
-};
-module_platform_driver(auok1901fb_driver);
-
-MODULE_DESCRIPTION("framebuffer driver for the AUO-K1901 EPD controller");
-MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>");
-MODULE_LICENSE("GPL");
+++ /dev/null
-/*
- * Common code for AUO-K190X framebuffer drivers
- *
- * Copyright (C) 2012 Heiko Stuebner <heiko@sntech.de>
- *
- * 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 <linux/module.h>
-#include <linux/sched/mm.h>
-#include <linux/kernel.h>
-#include <linux/gpio.h>
-#include <linux/platform_device.h>
-#include <linux/pm_runtime.h>
-#include <linux/fb.h>
-#include <linux/delay.h>
-#include <linux/uaccess.h>
-#include <linux/vmalloc.h>
-#include <linux/regulator/consumer.h>
-
-#include <video/auo_k190xfb.h>
-
-#include "auo_k190x.h"
-
-struct panel_info {
- int w;
- int h;
-};
-
-/* table of panel specific parameters to be indexed into by the board drivers */
-static struct panel_info panel_table[] = {
- /* standard 6" */
- [AUOK190X_RESOLUTION_800_600] = {
- .w = 800,
- .h = 600,
- },
- /* standard 9" */
- [AUOK190X_RESOLUTION_1024_768] = {
- .w = 1024,
- .h = 768,
- },
- [AUOK190X_RESOLUTION_600_800] = {
- .w = 600,
- .h = 800,
- },
- [AUOK190X_RESOLUTION_768_1024] = {
- .w = 768,
- .h = 1024,
- },
-};
-
-/*
- * private I80 interface to the board driver
- */
-
-static void auok190x_issue_data(struct auok190xfb_par *par, u16 data)
-{
- par->board->set_ctl(par, AUOK190X_I80_WR, 0);
- par->board->set_hdb(par, data);
- par->board->set_ctl(par, AUOK190X_I80_WR, 1);
-}
-
-static void auok190x_issue_cmd(struct auok190xfb_par *par, u16 data)
-{
- par->board->set_ctl(par, AUOK190X_I80_DC, 0);
- auok190x_issue_data(par, data);
- par->board->set_ctl(par, AUOK190X_I80_DC, 1);
-}
-
-/**
- * Conversion of 16bit color to 4bit grayscale
- * does roughly (0.3 * R + 0.6 G + 0.1 B) / 2
- */
-static inline int rgb565_to_gray4(u16 data, struct fb_var_screeninfo *var)
-{
- return ((((data & 0xF800) >> var->red.offset) * 77 +
- ((data & 0x07E0) >> (var->green.offset + 1)) * 151 +
- ((data & 0x1F) >> var->blue.offset) * 28) >> 8 >> 1);
-}
-
-static int auok190x_issue_pixels_rgb565(struct auok190xfb_par *par, int size,
- u16 *data)
-{
- struct fb_var_screeninfo *var = &par->info->var;
- struct device *dev = par->info->device;
- int i;
- u16 tmp;
-
- if (size & 7) {
- dev_err(dev, "issue_pixels: size %d must be a multiple of 8\n",
- size);
- return -EINVAL;
- }
-
- for (i = 0; i < (size >> 2); i++) {
- par->board->set_ctl(par, AUOK190X_I80_WR, 0);
-
- tmp = (rgb565_to_gray4(data[4*i], var) & 0x000F);
- tmp |= (rgb565_to_gray4(data[4*i+1], var) << 4) & 0x00F0;
- tmp |= (rgb565_to_gray4(data[4*i+2], var) << 8) & 0x0F00;
- tmp |= (rgb565_to_gray4(data[4*i+3], var) << 12) & 0xF000;
-
- par->board->set_hdb(par, tmp);
- par->board->set_ctl(par, AUOK190X_I80_WR, 1);
- }
-
- return 0;
-}
-
-static int auok190x_issue_pixels_gray8(struct auok190xfb_par *par, int size,
- u16 *data)
-{
- struct device *dev = par->info->device;
- int i;
- u16 tmp;
-
- if (size & 3) {
- dev_err(dev, "issue_pixels: size %d must be a multiple of 4\n",
- size);
- return -EINVAL;
- }
-
- for (i = 0; i < (size >> 1); i++) {
- par->board->set_ctl(par, AUOK190X_I80_WR, 0);
-
- /* simple reduction of 8bit staticgray to 4bit gray
- * combines 4 * 4bit pixel values into a 16bit value
- */
- tmp = (data[2*i] & 0xF0) >> 4;
- tmp |= (data[2*i] & 0xF000) >> 8;
- tmp |= (data[2*i+1] & 0xF0) << 4;
- tmp |= (data[2*i+1] & 0xF000);
-
- par->board->set_hdb(par, tmp);
- par->board->set_ctl(par, AUOK190X_I80_WR, 1);
- }
-
- return 0;
-}
-
-static int auok190x_issue_pixels(struct auok190xfb_par *par, int size,
- u16 *data)
-{
- struct fb_info *info = par->info;
- struct device *dev = par->info->device;
-
- if (info->var.bits_per_pixel == 8 && info->var.grayscale)
- auok190x_issue_pixels_gray8(par, size, data);
- else if (info->var.bits_per_pixel == 16)
- auok190x_issue_pixels_rgb565(par, size, data);
- else
- dev_err(dev, "unsupported color mode (bits: %d, gray: %d)\n",
- info->var.bits_per_pixel, info->var.grayscale);
-
- return 0;
-}
-
-static u16 auok190x_read_data(struct auok190xfb_par *par)
-{
- u16 data;
-
- par->board->set_ctl(par, AUOK190X_I80_OE, 0);
- data = par->board->get_hdb(par);
- par->board->set_ctl(par, AUOK190X_I80_OE, 1);
-
- return data;
-}
-
-/*
- * Command interface for the controller drivers
- */
-
-void auok190x_send_command_nowait(struct auok190xfb_par *par, u16 data)
-{
- par->board->set_ctl(par, AUOK190X_I80_CS, 0);
- auok190x_issue_cmd(par, data);
- par->board->set_ctl(par, AUOK190X_I80_CS, 1);
-}
-EXPORT_SYMBOL_GPL(auok190x_send_command_nowait);
-
-void auok190x_send_cmdargs_nowait(struct auok190xfb_par *par, u16 cmd,
- int argc, u16 *argv)
-{
- int i;
-
- par->board->set_ctl(par, AUOK190X_I80_CS, 0);
- auok190x_issue_cmd(par, cmd);
-
- for (i = 0; i < argc; i++)
- auok190x_issue_data(par, argv[i]);
- par->board->set_ctl(par, AUOK190X_I80_CS, 1);
-}
-EXPORT_SYMBOL_GPL(auok190x_send_cmdargs_nowait);
-
-int auok190x_send_command(struct auok190xfb_par *par, u16 data)
-{
- int ret;
-
- ret = par->board->wait_for_rdy(par);
- if (ret)
- return ret;
-
- auok190x_send_command_nowait(par, data);
- return 0;
-}
-EXPORT_SYMBOL_GPL(auok190x_send_command);
-
-int auok190x_send_cmdargs(struct auok190xfb_par *par, u16 cmd,
- int argc, u16 *argv)
-{
- int ret;
-
- ret = par->board->wait_for_rdy(par);
- if (ret)
- return ret;
-
- auok190x_send_cmdargs_nowait(par, cmd, argc, argv);
- return 0;
-}
-EXPORT_SYMBOL_GPL(auok190x_send_cmdargs);
-
-int auok190x_read_cmdargs(struct auok190xfb_par *par, u16 cmd,
- int argc, u16 *argv)
-{
- int i, ret;
-
- ret = par->board->wait_for_rdy(par);
- if (ret)
- return ret;
-
- par->board->set_ctl(par, AUOK190X_I80_CS, 0);
- auok190x_issue_cmd(par, cmd);
-
- for (i = 0; i < argc; i++)
- argv[i] = auok190x_read_data(par);
- par->board->set_ctl(par, AUOK190X_I80_CS, 1);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(auok190x_read_cmdargs);
-
-void auok190x_send_cmdargs_pixels_nowait(struct auok190xfb_par *par, u16 cmd,
- int argc, u16 *argv, int size, u16 *data)
-{
- int i;
-
- par->board->set_ctl(par, AUOK190X_I80_CS, 0);
-
- auok190x_issue_cmd(par, cmd);
-
- for (i = 0; i < argc; i++)
- auok190x_issue_data(par, argv[i]);
-
- auok190x_issue_pixels(par, size, data);
-
- par->board->set_ctl(par, AUOK190X_I80_CS, 1);
-}
-EXPORT_SYMBOL_GPL(auok190x_send_cmdargs_pixels_nowait);
-
-int auok190x_send_cmdargs_pixels(struct auok190xfb_par *par, u16 cmd,
- int argc, u16 *argv, int size, u16 *data)
-{
- int ret;
-
- ret = par->board->wait_for_rdy(par);
- if (ret)
- return ret;
-
- auok190x_send_cmdargs_pixels_nowait(par, cmd, argc, argv, size, data);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(auok190x_send_cmdargs_pixels);
-
-/*
- * fbdefio callbacks - common on both controllers.
- */
-
-static void auok190xfb_dpy_first_io(struct fb_info *info)
-{
- /* tell runtime-pm that we wish to use the device in a short time */
- pm_runtime_get(info->device);
-}
-
-/* this is called back from the deferred io workqueue */
-static void auok190xfb_dpy_deferred_io(struct fb_info *info,
- struct list_head *pagelist)
-{
- struct fb_deferred_io *fbdefio = info->fbdefio;
- struct auok190xfb_par *par = info->par;
- u16 line_length = info->fix.line_length;
- u16 yres = info->var.yres;
- u16 y1 = 0, h = 0;
- int prev_index = -1;
- struct page *cur;
- int h_inc;
- int threshold;
-
- if (!list_empty(pagelist))
- /* the device resume should've been requested through first_io,
- * if the resume did not finish until now, wait for it.
- */
- pm_runtime_barrier(info->device);
- else
- /* We reached this via the fsync or some other way.
- * In either case the first_io function did not run,
- * so we runtime_resume the device here synchronously.
- */
- pm_runtime_get_sync(info->device);
-
- /* Do a full screen update every n updates to prevent
- * excessive darkening of the Sipix display.
- * If we do this, there is no need to walk the pages.
- */
- if (par->need_refresh(par)) {
- par->update_all(par);
- goto out;
- }
-
- /* height increment is fixed per page */
- h_inc = DIV_ROUND_UP(PAGE_SIZE , line_length);
-
- /* calculate number of pages from pixel height */
- threshold = par->consecutive_threshold / h_inc;
- if (threshold < 1)
- threshold = 1;
-
- /* walk the written page list and swizzle the data */
- list_for_each_entry(cur, &fbdefio->pagelist, lru) {
- if (prev_index < 0) {
- /* just starting so assign first page */
- y1 = (cur->index << PAGE_SHIFT) / line_length;
- h = h_inc;
- } else if ((cur->index - prev_index) <= threshold) {
- /* page is within our threshold for single updates */
- h += h_inc * (cur->index - prev_index);
- } else {
- /* page not consecutive, issue previous update first */
- par->update_partial(par, y1, y1 + h);
-
- /* start over with our non consecutive page */
- y1 = (cur->index << PAGE_SHIFT) / line_length;
- h = h_inc;
- }
- prev_index = cur->index;
- }
-
- /* if we still have any pages to update we do so now */
- if (h >= yres)
- /* its a full screen update, just do it */
- par->update_all(par);
- else
- par->update_partial(par, y1, min((u16) (y1 + h), yres));
-
-out:
- pm_runtime_mark_last_busy(info->device);
- pm_runtime_put_autosuspend(info->device);
-}
-
-/*
- * framebuffer operations
- */
-
-/*
- * this is the slow path from userspace. they can seek and write to
- * the fb. it's inefficient to do anything less than a full screen draw
- */
-static ssize_t auok190xfb_write(struct fb_info *info, const char __user *buf,
- size_t count, loff_t *ppos)
-{
- struct auok190xfb_par *par = info->par;
- unsigned long p = *ppos;
- void *dst;
- int err = 0;
- unsigned long total_size;
-
- if (info->state != FBINFO_STATE_RUNNING)
- return -EPERM;
-
- total_size = info->fix.smem_len;
-
- if (p > total_size)
- return -EFBIG;
-
- if (count > total_size) {
- err = -EFBIG;
- count = total_size;
- }
-
- if (count + p > total_size) {
- if (!err)
- err = -ENOSPC;
-
- count = total_size - p;
- }
-
- dst = (void *)(info->screen_base + p);
-
- if (copy_from_user(dst, buf, count))
- err = -EFAULT;
-
- if (!err)
- *ppos += count;
-
- par->update_all(par);
-
- return (err) ? err : count;
-}
-
-static void auok190xfb_fillrect(struct fb_info *info,
- const struct fb_fillrect *rect)
-{
- struct auok190xfb_par *par = info->par;
-
- sys_fillrect(info, rect);
-
- par->update_all(par);
-}
-
-static void auok190xfb_copyarea(struct fb_info *info,
- const struct fb_copyarea *area)
-{
- struct auok190xfb_par *par = info->par;
-
- sys_copyarea(info, area);
-
- par->update_all(par);
-}
-
-static void auok190xfb_imageblit(struct fb_info *info,
- const struct fb_image *image)
-{
- struct auok190xfb_par *par = info->par;
-
- sys_imageblit(info, image);
-
- par->update_all(par);
-}
-
-static int auok190xfb_check_var(struct fb_var_screeninfo *var,
- struct fb_info *info)
-{
- struct device *dev = info->device;
- struct auok190xfb_par *par = info->par;
- struct panel_info *panel = &panel_table[par->resolution];
- int size;
-
- /*
- * Color depth
- */
-
- if (var->bits_per_pixel == 8 && var->grayscale == 1) {
- /*
- * For 8-bit grayscale, R, G, and B offset are equal.
- */
- var->red.length = 8;
- var->red.offset = 0;
- var->red.msb_right = 0;
-
- var->green.length = 8;
- var->green.offset = 0;
- var->green.msb_right = 0;
-
- var->blue.length = 8;
- var->blue.offset = 0;
- var->blue.msb_right = 0;
-
- var->transp.length = 0;
- var->transp.offset = 0;
- var->transp.msb_right = 0;
- } else if (var->bits_per_pixel == 16) {
- var->red.length = 5;
- var->red.offset = 11;
- var->red.msb_right = 0;
-
- var->green.length = 6;
- var->green.offset = 5;
- var->green.msb_right = 0;
-
- var->blue.length = 5;
- var->blue.offset = 0;
- var->blue.msb_right = 0;
-
- var->transp.length = 0;
- var->transp.offset = 0;
- var->transp.msb_right = 0;
- } else {
- dev_warn(dev, "unsupported color mode (bits: %d, grayscale: %d)\n",
- info->var.bits_per_pixel, info->var.grayscale);
- return -EINVAL;
- }
-
- /*
- * Dimensions
- */
-
- switch (var->rotate) {
- case FB_ROTATE_UR:
- case FB_ROTATE_UD:
- var->xres = panel->w;
- var->yres = panel->h;
- break;
- case FB_ROTATE_CW:
- case FB_ROTATE_CCW:
- var->xres = panel->h;
- var->yres = panel->w;
- break;
- default:
- dev_dbg(dev, "Invalid rotation request\n");
- return -EINVAL;
- }
-
- var->xres_virtual = var->xres;
- var->yres_virtual = var->yres;
-
- /*
- * Memory limit
- */
-
- size = var->xres_virtual * var->yres_virtual * var->bits_per_pixel / 8;
- if (size > info->fix.smem_len) {
- dev_err(dev, "Memory limit exceeded, requested %dK\n",
- size >> 10);
- return -ENOMEM;
- }
-
- return 0;
-}
-
-static int auok190xfb_set_fix(struct fb_info *info)
-{
- struct fb_fix_screeninfo *fix = &info->fix;
- struct fb_var_screeninfo *var = &info->var;
-
- fix->line_length = var->xres_virtual * var->bits_per_pixel / 8;
-
- fix->type = FB_TYPE_PACKED_PIXELS;
- fix->accel = FB_ACCEL_NONE;
- fix->visual = (var->grayscale) ? FB_VISUAL_STATIC_PSEUDOCOLOR
- : FB_VISUAL_TRUECOLOR;
- fix->xpanstep = 0;
- fix->ypanstep = 0;
- fix->ywrapstep = 0;
-
- return 0;
-}
-
-static int auok190xfb_set_par(struct fb_info *info)
-{
- struct auok190xfb_par *par = info->par;
-
- par->rotation = info->var.rotate;
- auok190xfb_set_fix(info);
-
- /* reinit the controller to honor the rotation */
- par->init(par);
-
- /* wait for init to complete */
- par->board->wait_for_rdy(par);
-
- return 0;
-}
-
-static struct fb_ops auok190xfb_ops = {
- .owner = THIS_MODULE,
- .fb_read = fb_sys_read,
- .fb_write = auok190xfb_write,
- .fb_fillrect = auok190xfb_fillrect,
- .fb_copyarea = auok190xfb_copyarea,
- .fb_imageblit = auok190xfb_imageblit,
- .fb_check_var = auok190xfb_check_var,
- .fb_set_par = auok190xfb_set_par,
-};
-
-/*
- * Controller-functions common to both K1900 and K1901
- */
-
-static int auok190x_read_temperature(struct auok190xfb_par *par)
-{
- struct device *dev = par->info->device;
- u16 data[4];
- int temp;
-
- pm_runtime_get_sync(dev);
-
- mutex_lock(&(par->io_lock));
-
- auok190x_read_cmdargs(par, AUOK190X_CMD_READ_VERSION, 4, data);
-
- mutex_unlock(&(par->io_lock));
-
- pm_runtime_mark_last_busy(dev);
- pm_runtime_put_autosuspend(dev);
-
- /* sanitize and split of half-degrees for now */
- temp = ((data[0] & AUOK190X_VERSION_TEMP_MASK) >> 1);
-
- /* handle positive and negative temperatures */
- if (temp >= 201)
- return (255 - temp + 1) * (-1);
- else
- return temp;
-}
-
-static void auok190x_identify(struct auok190xfb_par *par)
-{
- struct device *dev = par->info->device;
- u16 data[4];
-
- pm_runtime_get_sync(dev);
-
- mutex_lock(&(par->io_lock));
-
- auok190x_read_cmdargs(par, AUOK190X_CMD_READ_VERSION, 4, data);
-
- mutex_unlock(&(par->io_lock));
-
- par->epd_type = data[1] & AUOK190X_VERSION_TEMP_MASK;
-
- par->panel_size_int = AUOK190X_VERSION_SIZE_INT(data[2]);
- par->panel_size_float = AUOK190X_VERSION_SIZE_FLOAT(data[2]);
- par->panel_model = AUOK190X_VERSION_MODEL(data[2]);
-
- par->tcon_version = AUOK190X_VERSION_TCON(data[3]);
- par->lut_version = AUOK190X_VERSION_LUT(data[3]);
-
- dev_dbg(dev, "panel %d.%din, model 0x%x, EPD 0x%x TCON-rev 0x%x, LUT-rev 0x%x",
- par->panel_size_int, par->panel_size_float, par->panel_model,
- par->epd_type, par->tcon_version, par->lut_version);
-
- pm_runtime_mark_last_busy(dev);
- pm_runtime_put_autosuspend(dev);
-}
-
-/*
- * Sysfs functions
- */
-
-static ssize_t update_mode_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct fb_info *info = dev_get_drvdata(dev);
- struct auok190xfb_par *par = info->par;
-
- return sprintf(buf, "%d\n", par->update_mode);
-}
-
-static ssize_t update_mode_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct fb_info *info = dev_get_drvdata(dev);
- struct auok190xfb_par *par = info->par;
- int mode, ret;
-
- ret = kstrtoint(buf, 10, &mode);
- if (ret)
- return ret;
-
- par->update_mode = mode;
-
- /* if we enter a better mode, do a full update */
- if (par->last_mode > 1 && mode < par->last_mode)
- par->update_all(par);
-
- return count;
-}
-
-static ssize_t flash_show(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- struct fb_info *info = dev_get_drvdata(dev);
- struct auok190xfb_par *par = info->par;
-
- return sprintf(buf, "%d\n", par->flash);
-}
-
-static ssize_t flash_store(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct fb_info *info = dev_get_drvdata(dev);
- struct auok190xfb_par *par = info->par;
- int flash, ret;
-
- ret = kstrtoint(buf, 10, &flash);
- if (ret)
- return ret;
-
- if (flash > 0)
- par->flash = 1;
- else
- par->flash = 0;
-
- return count;
-}
-
-static ssize_t temp_show(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- struct fb_info *info = dev_get_drvdata(dev);
- struct auok190xfb_par *par = info->par;
- int temp;
-
- temp = auok190x_read_temperature(par);
- return sprintf(buf, "%d\n", temp);
-}
-
-static DEVICE_ATTR_RW(update_mode);
-static DEVICE_ATTR_RW(flash);
-static DEVICE_ATTR(temp, 0644, temp_show, NULL);
-
-static struct attribute *auok190x_attributes[] = {
- &dev_attr_update_mode.attr,
- &dev_attr_flash.attr,
- &dev_attr_temp.attr,
- NULL
-};
-
-static const struct attribute_group auok190x_attr_group = {
- .attrs = auok190x_attributes,
-};
-
-static int auok190x_power(struct auok190xfb_par *par, bool on)
-{
- struct auok190x_board *board = par->board;
- int ret;
-
- if (on) {
- /* We should maintain POWER up for at least 80ms before set
- * RST_N and SLP_N to high (TCON spec 20100803_v35 p59)
- */
- ret = regulator_enable(par->regulator);
- if (ret)
- return ret;
-
- msleep(200);
- gpio_set_value(board->gpio_nrst, 1);
- gpio_set_value(board->gpio_nsleep, 1);
- msleep(200);
- } else {
- regulator_disable(par->regulator);
- gpio_set_value(board->gpio_nrst, 0);
- gpio_set_value(board->gpio_nsleep, 0);
- }
-
- return 0;
-}
-
-/*
- * Recovery - powercycle the controller
- */
-
-static void auok190x_recover(struct auok190xfb_par *par)
-{
- struct device *dev = par->info->device;
-
- auok190x_power(par, 0);
- msleep(100);
- auok190x_power(par, 1);
-
- /* after powercycling the device, it's always active */
- pm_runtime_set_active(dev);
- par->standby = 0;
-
- par->init(par);
-
- /* wait for init to complete */
- par->board->wait_for_rdy(par);
-}
-
-/*
- * Power-management
- */
-static int __maybe_unused auok190x_runtime_suspend(struct device *dev)
-{
- struct platform_device *pdev = to_platform_device(dev);
- struct fb_info *info = platform_get_drvdata(pdev);
- struct auok190xfb_par *par = info->par;
- struct auok190x_board *board = par->board;
- u16 standby_param;
-
- /* take and keep the lock until we are resumed, as the controller
- * will never reach the non-busy state when in standby mode
- */
- mutex_lock(&(par->io_lock));
-
- if (par->standby) {
- dev_warn(dev, "already in standby, runtime-pm pairing mismatch\n");
- mutex_unlock(&(par->io_lock));
- return 0;
- }
-
- /* according to runtime_pm.txt runtime_suspend only means, that the
- * device will not process data and will not communicate with the CPU
- * As we hold the lock, this stays true even without standby
- */
- if (board->quirks & AUOK190X_QUIRK_STANDBYBROKEN) {
- dev_dbg(dev, "runtime suspend without standby\n");
- goto finish;
- } else if (board->quirks & AUOK190X_QUIRK_STANDBYPARAM) {
- /* for some TCON versions STANDBY expects a parameter (0) but
- * it seems the real tcon version has to be determined yet.
- */
- dev_dbg(dev, "runtime suspend with additional empty param\n");
- standby_param = 0;
- auok190x_send_cmdargs(par, AUOK190X_CMD_STANDBY, 1,
- &standby_param);
- } else {
- dev_dbg(dev, "runtime suspend without param\n");
- auok190x_send_command(par, AUOK190X_CMD_STANDBY);
- }
-
- msleep(64);
-
-finish:
- par->standby = 1;
-
- return 0;
-}
-
-static int __maybe_unused auok190x_runtime_resume(struct device *dev)
-{
- struct platform_device *pdev = to_platform_device(dev);
- struct fb_info *info = platform_get_drvdata(pdev);
- struct auok190xfb_par *par = info->par;
- struct auok190x_board *board = par->board;
-
- if (!par->standby) {
- dev_warn(dev, "not in standby, runtime-pm pairing mismatch\n");
- return 0;
- }
-
- if (board->quirks & AUOK190X_QUIRK_STANDBYBROKEN) {
- dev_dbg(dev, "runtime resume without standby\n");
- } else {
- /* when in standby, controller is always busy
- * and only accepts the wakeup command
- */
- dev_dbg(dev, "runtime resume from standby\n");
- auok190x_send_command_nowait(par, AUOK190X_CMD_WAKEUP);
-
- msleep(160);
-
- /* wait for the controller to be ready and release the lock */
- board->wait_for_rdy(par);
- }
-
- par->standby = 0;
-
- mutex_unlock(&(par->io_lock));
-
- return 0;
-}
-
-static int __maybe_unused auok190x_suspend(struct device *dev)
-{
- struct platform_device *pdev = to_platform_device(dev);
- struct fb_info *info = platform_get_drvdata(pdev);
- struct auok190xfb_par *par = info->par;
- struct auok190x_board *board = par->board;
- int ret;
-
- dev_dbg(dev, "suspend\n");
- if (board->quirks & AUOK190X_QUIRK_STANDBYBROKEN) {
- /* suspend via powering off the ic */
- dev_dbg(dev, "suspend with broken standby\n");
-
- auok190x_power(par, 0);
- } else {
- dev_dbg(dev, "suspend using sleep\n");
-
- /* the sleep state can only be entered from the standby state.
- * pm_runtime_get_noresume gets called before the suspend call.
- * So the devices usage count is >0 but it is not necessarily
- * active.
- */
- if (!pm_runtime_status_suspended(dev)) {
- ret = auok190x_runtime_suspend(dev);
- if (ret < 0) {
- dev_err(dev, "auok190x_runtime_suspend failed with %d\n",
- ret);
- return ret;
- }
- par->manual_standby = 1;
- }
-
- gpio_direction_output(board->gpio_nsleep, 0);
- }
-
- msleep(100);
-
- return 0;
-}
-
-static int __maybe_unused auok190x_resume(struct device *dev)
-{
- struct platform_device *pdev = to_platform_device(dev);
- struct fb_info *info = platform_get_drvdata(pdev);
- struct auok190xfb_par *par = info->par;
- struct auok190x_board *board = par->board;
-
- dev_dbg(dev, "resume\n");
- if (board->quirks & AUOK190X_QUIRK_STANDBYBROKEN) {
- dev_dbg(dev, "resume with broken standby\n");
-
- auok190x_power(par, 1);
-
- par->init(par);
- } else {
- dev_dbg(dev, "resume from sleep\n");
-
- /* device should be in runtime suspend when we were suspended
- * and pm_runtime_put_sync gets called after this function.
- * So there is no need to touch the standby mode here at all.
- */
- gpio_direction_output(board->gpio_nsleep, 1);
- msleep(100);
-
- /* an additional init call seems to be necessary after sleep */
- auok190x_runtime_resume(dev);
- par->init(par);
-
- /* if we were runtime-suspended before, suspend again*/
- if (!par->manual_standby)
- auok190x_runtime_suspend(dev);
- else
- par->manual_standby = 0;
- }
-
- return 0;
-}
-
-const struct dev_pm_ops auok190x_pm = {
- SET_RUNTIME_PM_OPS(auok190x_runtime_suspend, auok190x_runtime_resume,
- NULL)
- SET_SYSTEM_SLEEP_PM_OPS(auok190x_suspend, auok190x_resume)
-};
-EXPORT_SYMBOL_GPL(auok190x_pm);
-
-/*
- * Common probe and remove code
- */
-
-int auok190x_common_probe(struct platform_device *pdev,
- struct auok190x_init_data *init)
-{
- struct auok190x_board *board = init->board;
- struct auok190xfb_par *par;
- struct fb_info *info;
- struct panel_info *panel;
- int videomemorysize, ret;
- unsigned char *videomemory;
-
- /* check board contents */
- if (!board->init || !board->cleanup || !board->wait_for_rdy
- || !board->set_ctl || !board->set_hdb || !board->get_hdb
- || !board->setup_irq)
- return -EINVAL;
-
- info = framebuffer_alloc(sizeof(struct auok190xfb_par), &pdev->dev);
- if (!info)
- return -ENOMEM;
-
- par = info->par;
- par->info = info;
- par->board = board;
- par->recover = auok190x_recover;
- par->update_partial = init->update_partial;
- par->update_all = init->update_all;
- par->need_refresh = init->need_refresh;
- par->init = init->init;
-
- /* init update modes */
- par->update_cnt = 0;
- par->update_mode = -1;
- par->last_mode = -1;
- par->flash = 0;
-
- par->regulator = regulator_get(info->device, "vdd");
- if (IS_ERR(par->regulator)) {
- ret = PTR_ERR(par->regulator);
- dev_err(info->device, "Failed to get regulator: %d\n", ret);
- goto err_reg;
- }
-
- ret = board->init(par);
- if (ret) {
- dev_err(info->device, "board init failed, %d\n", ret);
- goto err_board;
- }
-
- ret = gpio_request(board->gpio_nsleep, "AUOK190x sleep");
- if (ret) {
- dev_err(info->device, "could not request sleep gpio, %d\n",
- ret);
- goto err_gpio1;
- }
-
- ret = gpio_direction_output(board->gpio_nsleep, 0);
- if (ret) {
- dev_err(info->device, "could not set sleep gpio, %d\n", ret);
- goto err_gpio2;
- }
-
- ret = gpio_request(board->gpio_nrst, "AUOK190x reset");
- if (ret) {
- dev_err(info->device, "could not request reset gpio, %d\n",
- ret);
- goto err_gpio2;
- }
-
- ret = gpio_direction_output(board->gpio_nrst, 0);
- if (ret) {
- dev_err(info->device, "could not set reset gpio, %d\n", ret);
- goto err_gpio3;
- }
-
- ret = auok190x_power(par, 1);
- if (ret) {
- dev_err(info->device, "could not power on the device, %d\n",
- ret);
- goto err_gpio3;
- }
-
- mutex_init(&par->io_lock);
-
- init_waitqueue_head(&par->waitq);
-
- ret = par->board->setup_irq(par->info);
- if (ret) {
- dev_err(info->device, "could not setup ready-irq, %d\n", ret);
- goto err_irq;
- }
-
- /* wait for init to complete */
- par->board->wait_for_rdy(par);
-
- /*
- * From here on the controller can talk to us
- */
-
- /* initialise fix, var, resolution and rotation */
-
- strlcpy(info->fix.id, init->id, 16);
- info->var.bits_per_pixel = 8;
- info->var.grayscale = 1;
-
- panel = &panel_table[board->resolution];
-
- par->resolution = board->resolution;
- par->rotation = 0;
-
- /* videomemory handling */
-
- videomemorysize = roundup((panel->w * panel->h) * 2, PAGE_SIZE);
- videomemory = vzalloc(videomemorysize);
- if (!videomemory) {
- ret = -ENOMEM;
- goto err_irq;
- }
-
- info->screen_base = (char *)videomemory;
- info->fix.smem_len = videomemorysize;
-
- info->flags = FBINFO_FLAG_DEFAULT | FBINFO_VIRTFB;
- info->fbops = &auok190xfb_ops;
-
- ret = auok190xfb_check_var(&info->var, info);
- if (ret)
- goto err_defio;
-
- auok190xfb_set_fix(info);
-
- /* deferred io init */
-
- info->fbdefio = devm_kzalloc(info->device,
- sizeof(struct fb_deferred_io),
- GFP_KERNEL);
- if (!info->fbdefio) {
- dev_err(info->device, "Failed to allocate memory\n");
- ret = -ENOMEM;
- goto err_defio;
- }
-
- dev_dbg(info->device, "targeting %d frames per second\n", board->fps);
- info->fbdefio->delay = HZ / board->fps;
- info->fbdefio->first_io = auok190xfb_dpy_first_io,
- info->fbdefio->deferred_io = auok190xfb_dpy_deferred_io,
- fb_deferred_io_init(info);
-
- /* color map */
-
- ret = fb_alloc_cmap(&info->cmap, 256, 0);
- if (ret < 0) {
- dev_err(info->device, "Failed to allocate colormap\n");
- goto err_cmap;
- }
-
- /* controller init */
-
- par->consecutive_threshold = 100;
- par->init(par);
- auok190x_identify(par);
-
- platform_set_drvdata(pdev, info);
-
- ret = register_framebuffer(info);
- if (ret < 0)
- goto err_regfb;
-
- ret = sysfs_create_group(&info->device->kobj, &auok190x_attr_group);
- if (ret)
- goto err_sysfs;
-
- dev_info(info->device, "fb%d: %dx%d using %dK of video memory\n",
- info->node, info->var.xres, info->var.yres,
- videomemorysize >> 10);
-
- /* increase autosuspend_delay when we use alternative methods
- * for runtime_pm
- */
- par->autosuspend_delay = (board->quirks & AUOK190X_QUIRK_STANDBYBROKEN)
- ? 1000 : 200;
-
- pm_runtime_set_active(info->device);
- pm_runtime_enable(info->device);
- pm_runtime_set_autosuspend_delay(info->device, par->autosuspend_delay);
- pm_runtime_use_autosuspend(info->device);
-
- return 0;
-
-err_sysfs:
- unregister_framebuffer(info);
-err_regfb:
- fb_dealloc_cmap(&info->cmap);
-err_cmap:
- fb_deferred_io_cleanup(info);
-err_defio:
- vfree((void *)info->screen_base);
-err_irq:
- auok190x_power(par, 0);
-err_gpio3:
- gpio_free(board->gpio_nrst);
-err_gpio2:
- gpio_free(board->gpio_nsleep);
-err_gpio1:
- board->cleanup(par);
-err_board:
- regulator_put(par->regulator);
-err_reg:
- framebuffer_release(info);
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(auok190x_common_probe);
-
-int auok190x_common_remove(struct platform_device *pdev)
-{
- struct fb_info *info = platform_get_drvdata(pdev);
- struct auok190xfb_par *par = info->par;
- struct auok190x_board *board = par->board;
-
- pm_runtime_disable(info->device);
-
- sysfs_remove_group(&info->device->kobj, &auok190x_attr_group);
-
- unregister_framebuffer(info);
-
- fb_dealloc_cmap(&info->cmap);
-
- fb_deferred_io_cleanup(info);
-
- vfree((void *)info->screen_base);
-
- auok190x_power(par, 0);
-
- gpio_free(board->gpio_nrst);
- gpio_free(board->gpio_nsleep);
-
- board->cleanup(par);
-
- regulator_put(par->regulator);
-
- framebuffer_release(info);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(auok190x_common_remove);
-
-MODULE_DESCRIPTION("Common code for AUO-K190X controllers");
-MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>");
-MODULE_LICENSE("GPL");
+++ /dev/null
-/*
- * Private common definitions for AUO-K190X framebuffer drivers
- *
- * Copyright (C) 2012 Heiko Stuebner <heiko@sntech.de>
- *
- * 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.
- */
-
-/*
- * I80 interface specific defines
- */
-
-#define AUOK190X_I80_CS 0x01
-#define AUOK190X_I80_DC 0x02
-#define AUOK190X_I80_WR 0x03
-#define AUOK190X_I80_OE 0x04
-
-/*
- * AUOK190x commands, common to both controllers
- */
-
-#define AUOK190X_CMD_INIT 0x0000
-#define AUOK190X_CMD_STANDBY 0x0001
-#define AUOK190X_CMD_WAKEUP 0x0002
-#define AUOK190X_CMD_TCON_RESET 0x0003
-#define AUOK190X_CMD_DATA_STOP 0x1002
-#define AUOK190X_CMD_LUT_START 0x1003
-#define AUOK190X_CMD_DISP_REFRESH 0x1004
-#define AUOK190X_CMD_DISP_RESET 0x1005
-#define AUOK190X_CMD_PRE_DISPLAY_START 0x100D
-#define AUOK190X_CMD_PRE_DISPLAY_STOP 0x100F
-#define AUOK190X_CMD_FLASH_W 0x2000
-#define AUOK190X_CMD_FLASH_E 0x2001
-#define AUOK190X_CMD_FLASH_STS 0x2002
-#define AUOK190X_CMD_FRAMERATE 0x3000
-#define AUOK190X_CMD_READ_VERSION 0x4000
-#define AUOK190X_CMD_READ_STATUS 0x4001
-#define AUOK190X_CMD_READ_LUT 0x4003
-#define AUOK190X_CMD_DRIVERTIMING 0x5000
-#define AUOK190X_CMD_LBALANCE 0x5001
-#define AUOK190X_CMD_AGINGMODE 0x6000
-#define AUOK190X_CMD_AGINGEXIT 0x6001
-
-/*
- * Common settings for AUOK190X_CMD_INIT
- */
-
-#define AUOK190X_INIT_DATA_FILTER (0 << 12)
-#define AUOK190X_INIT_DATA_BYPASS (1 << 12)
-#define AUOK190X_INIT_INVERSE_WHITE (0 << 9)
-#define AUOK190X_INIT_INVERSE_BLACK (1 << 9)
-#define AUOK190X_INIT_SCAN_DOWN (0 << 1)
-#define AUOK190X_INIT_SCAN_UP (1 << 1)
-#define AUOK190X_INIT_SHIFT_LEFT (0 << 0)
-#define AUOK190X_INIT_SHIFT_RIGHT (1 << 0)
-
-/* Common bits to pixels
- * Mode 15-12 11-8 7-4 3-0
- * format0 4 3 2 1
- * format1 3 4 1 2
- */
-
-#define AUOK190X_INIT_FORMAT0 0
-#define AUOK190X_INIT_FORMAT1 (1 << 6)
-
-/*
- * settings for AUOK190X_CMD_RESET
- */
-
-#define AUOK190X_RESET_TCON (0 << 0)
-#define AUOK190X_RESET_NORMAL (1 << 0)
-#define AUOK190X_RESET_PON (1 << 1)
-
-/*
- * AUOK190X_CMD_VERSION
- */
-
-#define AUOK190X_VERSION_TEMP_MASK (0x1ff)
-#define AUOK190X_VERSION_EPD_MASK (0xff)
-#define AUOK190X_VERSION_SIZE_INT(_val) ((_val & 0xfc00) >> 10)
-#define AUOK190X_VERSION_SIZE_FLOAT(_val) ((_val & 0x3c0) >> 6)
-#define AUOK190X_VERSION_MODEL(_val) (_val & 0x3f)
-#define AUOK190X_VERSION_LUT(_val) (_val & 0xff)
-#define AUOK190X_VERSION_TCON(_val) ((_val & 0xff00) >> 8)
-
-/*
- * update modes for CMD_PARTIALDISP on K1900 and CMD_DDMA on K1901
- */
-
-#define AUOK190X_UPDATE_MODE(_res) ((_res & 0x7) << 12)
-#define AUOK190X_UPDATE_NONFLASH (1 << 15)
-
-/*
- * track panel specific parameters for common init
- */
-
-struct auok190x_init_data {
- char *id;
- struct auok190x_board *board;
-
- void (*update_partial)(struct auok190xfb_par *par, u16 y1, u16 y2);
- void (*update_all)(struct auok190xfb_par *par);
- bool (*need_refresh)(struct auok190xfb_par *par);
- void (*init)(struct auok190xfb_par *par);
-};
-
-
-extern void auok190x_send_command_nowait(struct auok190xfb_par *par, u16 data);
-extern int auok190x_send_command(struct auok190xfb_par *par, u16 data);
-extern void auok190x_send_cmdargs_nowait(struct auok190xfb_par *par, u16 cmd,
- int argc, u16 *argv);
-extern int auok190x_send_cmdargs(struct auok190xfb_par *par, u16 cmd,
- int argc, u16 *argv);
-extern void auok190x_send_cmdargs_pixels_nowait(struct auok190xfb_par *par,
- u16 cmd, int argc, u16 *argv,
- int size, u16 *data);
-extern int auok190x_send_cmdargs_pixels(struct auok190xfb_par *par, u16 cmd,
- int argc, u16 *argv, int size,
- u16 *data);
-extern int auok190x_read_cmdargs(struct auok190xfb_par *par, u16 cmd,
- int argc, u16 *argv);
-
-extern int auok190x_common_probe(struct platform_device *pdev,
- struct auok190x_init_data *init);
-extern int auok190x_common_remove(struct platform_device *pdev);
-
-extern const struct dev_pm_ops auok190x_pm;
}
/* this is to find and return the vmalloc-ed fb pages */
-static int fb_deferred_io_fault(struct vm_fault *vmf)
+static vm_fault_t fb_deferred_io_fault(struct vm_fault *vmf)
{
unsigned long offset;
struct page *page;
EXPORT_SYMBOL_GPL(fb_deferred_io_fsync);
/* vm_ops->page_mkwrite handler */
-static int fb_deferred_io_mkwrite(struct vm_fault *vmf)
+static vm_fault_t fb_deferred_io_mkwrite(struct vm_fault *vmf)
{
struct page *page = vmf->page;
struct fb_info *info = vmf->vma->vm_private_data;
/* put videomode list to info structure */
videomodes = kcalloc(videomode_num, sizeof(struct fb_videomode),
GFP_KERNEL);
- if (!videomodes) {
- dev_err(fbi->dev, "can't malloc video modes\n");
+ if (!videomodes)
return -ENOMEM;
- }
+
for (i = 0; i < videomode_num; i++)
mmpmode_to_fbmode(&videomodes[i], &mmp_modes[i]);
fb_videomode_to_modelist(videomodes, videomode_num, &info->modelist);
dev_info(ctrl->dev, "%s: %s\n", __func__, config->name);
/* init driver data */
- path_info = kzalloc(sizeof(struct mmp_path_info), GFP_KERNEL);
- if (!path_info) {
- dev_err(ctrl->dev, "%s: unable to alloc path_info for %s\n",
- __func__, config->name);
+ path_info = kzalloc(sizeof(*path_info), GFP_KERNEL);
+ if (!path_info)
return 0;
- }
+
path_info->name = config->name;
path_info->id = path_plat->id;
path_info->dev = ctrl->dev;
"(default=0)");
module_param(noscale, int, 0);
MODULE_PARM_DESC(noscale,
- "Disables screen scaleing. (0 or 1=disable) "
+ "Disables screen scaling. (0 or 1=disable) "
"(default=0, do scaling)");
module_param(paneltweak, int, 0);
MODULE_PARM_DESC(paneltweak,
};
module_platform_driver(ams_delta_panel_driver);
+
+MODULE_AUTHOR("Jonathan McDowell <noodles@earth.li>");
+MODULE_DESCRIPTION("LCD panel support for the Amstrad E3 (Delta) videophone");
+MODULE_LICENSE("GPL");
};
module_platform_driver(h3_panel_driver);
+
+MODULE_AUTHOR("Imre Deak");
+MODULE_DESCRIPTION("LCD panel support for the TI OMAP H3 board");
+MODULE_LICENSE("GPL");
};
module_platform_driver(htcherald_panel_driver);
+
+MODULE_AUTHOR("Cory Maccarrone");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("LCD panel support for the HTC Herald");
};
module_platform_driver(innovator1510_panel_driver);
+
+MODULE_AUTHOR("Imre Deak");
+MODULE_DESCRIPTION("LCD panel support for the TI OMAP1510 Innovator board");
+MODULE_LICENSE("GPL");
};
module_platform_driver(innovator1610_panel_driver);
+
+MODULE_AUTHOR("Imre Deak");
+MODULE_DESCRIPTION("LCD panel support for the TI OMAP1610 Innovator board");
+MODULE_LICENSE("GPL");
};
module_platform_driver(osk_panel_driver);
+
+MODULE_AUTHOR("Imre Deak");
+MODULE_DESCRIPTION("LCD panel support for the TI OMAP OSK board");
+MODULE_LICENSE("GPL");
};
module_platform_driver(palmte_panel_driver);
+
+MODULE_AUTHOR("Romain Goyet <r.goyet@gmail.com>, Laurent Gonzalez <palmte.linux@free.fr>");
+MODULE_DESCRIPTION("LCD panel support for the Palm Tungsten E");
+MODULE_LICENSE("GPL");
};
module_platform_driver(palmtt_panel_driver);
+
+MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>");
+MODULE_DESCRIPTION("LCD panel support for Palm Tungsten|T");
+MODULE_LICENSE("GPL");
};
module_platform_driver(palmz71_panel_driver);
+
+MODULE_AUTHOR("Romain Goyet, Laurent Gonzalez, Marek Vasut");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("LCD panel support for the Palm Zire71");
goto cleanup;
}
- fbdev = kzalloc(sizeof(struct omapfb_device), GFP_KERNEL);
+ fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL);
if (fbdev == NULL) {
dev_err(&pdev->dev,
"unable to allocate memory for device info\n");
tristate "OMAP2+ frame buffer support"
depends on FB
depends on DRM_OMAP = n
+ depends on GPIOLIB
select FB_OMAP2_DSS
select OMAP2_VRFB if ARCH_OMAP2 || ARCH_OMAP3
static ssize_t dsicm_num_errors_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct panel_drv_data *ddata = platform_get_drvdata(pdev);
+ struct panel_drv_data *ddata = dev_get_drvdata(dev);
struct omap_dss_device *in = ddata->in;
u8 errors = 0;
int r;
static ssize_t dsicm_hw_revision_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct panel_drv_data *ddata = platform_get_drvdata(pdev);
+ struct panel_drv_data *ddata = dev_get_drvdata(dev);
struct omap_dss_device *in = ddata->in;
u8 id1, id2, id3;
int r;
struct device_attribute *attr,
const char *buf, size_t count)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct panel_drv_data *ddata = platform_get_drvdata(pdev);
+ struct panel_drv_data *ddata = dev_get_drvdata(dev);
struct omap_dss_device *in = ddata->in;
unsigned long t;
int r;
struct device_attribute *attr,
char *buf)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct panel_drv_data *ddata = platform_get_drvdata(pdev);
+ struct panel_drv_data *ddata = dev_get_drvdata(dev);
unsigned t;
mutex_lock(&ddata->lock);
struct device_attribute *attr,
const char *buf, size_t count)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct panel_drv_data *ddata = platform_get_drvdata(pdev);
+ struct panel_drv_data *ddata = dev_get_drvdata(dev);
struct omap_dss_device *in = ddata->in;
unsigned long t;
int r;
struct device_attribute *attr,
char *buf)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct panel_drv_data *ddata = platform_get_drvdata(pdev);
+ struct panel_drv_data *ddata = dev_get_drvdata(dev);
unsigned t;
mutex_lock(&ddata->lock);
if (ret)
s = "color-tft";
- for (i = 0; lcd_types[i]; i++)
- if (!strcmp(s, lcd_types[i]))
- break;
- if (!i || !lcd_types[i]) {
+ i = match_string(lcd_types, -1, s);
+ if (i < 0) {
dev_err(dev, "lcd-type %s is unknown\n", s);
- return -EINVAL;
+ return i;
}
info->lcd_conn |= LCD_CONN_TYPE(i);
info->lcd_conn |= LCD_CONN_WIDTH(bus_width);
vga_out8(0x3d4, 0x66, par);
cr66 = vga_in8(0x3d5, par);
vga_out8(0x3d5, cr66 | 0x02, par);
- mdelay(10);
+ usleep_range(10000, 11000);
vga_out8(0x3d4, 0x66, par);
vga_out8(0x3d5, cr66 & ~0x02, par); /* clear reset flag */
- mdelay(10);
+ usleep_range(10000, 11000);
/*
vga_out8(0x3d4, 0x3f, par);
cr3f = vga_in8(0x3d5, par);
vga_out8(0x3d5, cr3f | 0x08, par);
- mdelay(10);
+ usleep_range(10000, 11000);
vga_out8(0x3d4, 0x3f, par);
vga_out8(0x3d5, cr3f & ~0x08, par); /* clear reset flags */
- mdelay(10);
+ usleep_range(10000, 11000);
/* Savage ramdac speeds */
par->numClocks = 4;
#include <linux/vmalloc.h>
#include <video/sh_mobile_lcdc.h>
-#include <video/sh_mobile_meram.h>
#include "sh_mobile_lcdcfb.h"
struct notifier_block notifier;
int started;
int forced_fourcc; /* 2 channel LCDC must share fourcc setting */
- struct sh_mobile_meram_info *meram_dev;
};
/* -----------------------------------------------------------------------------
if (priv->dot_clk)
clk_prepare_enable(priv->dot_clk);
pm_runtime_get_sync(priv->dev);
- if (priv->meram_dev && priv->meram_dev->pdev)
- pm_runtime_get_sync(&priv->meram_dev->pdev->dev);
}
}
static void sh_mobile_lcdc_clk_off(struct sh_mobile_lcdc_priv *priv)
{
if (atomic_sub_return(1, &priv->hw_usecnt) == -1) {
- if (priv->meram_dev && priv->meram_dev->pdev)
- pm_runtime_put_sync(&priv->meram_dev->pdev->dev);
pm_runtime_put(priv->dev);
if (priv->dot_clk)
clk_disable_unprepare(priv->dot_clk);
static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
{
- struct sh_mobile_meram_info *mdev = priv->meram_dev;
struct sh_mobile_lcdc_chan *ch;
unsigned long tmp;
int ret;
/* Compute frame buffer base address and pitch for each channel. */
for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
- int pixelformat;
- void *cache;
-
ch = &priv->ch[k];
if (!ch->enabled)
continue;
ch->base_addr_c = ch->dma_handle
+ ch->xres_virtual * ch->yres_virtual;
ch->line_size = ch->pitch;
-
- /* Enable MERAM if possible. */
- if (mdev == NULL || ch->cfg->meram_cfg == NULL)
- continue;
-
- /* Free the allocated MERAM cache. */
- if (ch->cache) {
- sh_mobile_meram_cache_free(mdev, ch->cache);
- ch->cache = NULL;
- }
-
- switch (ch->format->fourcc) {
- case V4L2_PIX_FMT_NV12:
- case V4L2_PIX_FMT_NV21:
- case V4L2_PIX_FMT_NV16:
- case V4L2_PIX_FMT_NV61:
- pixelformat = SH_MOBILE_MERAM_PF_NV;
- break;
- case V4L2_PIX_FMT_NV24:
- case V4L2_PIX_FMT_NV42:
- pixelformat = SH_MOBILE_MERAM_PF_NV24;
- break;
- case V4L2_PIX_FMT_RGB565:
- case V4L2_PIX_FMT_BGR24:
- case V4L2_PIX_FMT_BGR32:
- default:
- pixelformat = SH_MOBILE_MERAM_PF_RGB;
- break;
- }
-
- cache = sh_mobile_meram_cache_alloc(mdev, ch->cfg->meram_cfg,
- ch->pitch, ch->yres, pixelformat,
- &ch->line_size);
- if (!IS_ERR(cache)) {
- sh_mobile_meram_cache_update(mdev, cache,
- ch->base_addr_y, ch->base_addr_c,
- &ch->base_addr_y, &ch->base_addr_c);
- ch->cache = cache;
- }
}
for (k = 0; k < ARRAY_SIZE(priv->overlays); ++k) {
}
sh_mobile_lcdc_display_off(ch);
-
- /* Free the MERAM cache. */
- if (ch->cache) {
- sh_mobile_meram_cache_free(priv->meram_dev, ch->cache);
- ch->cache = NULL;
- }
-
}
/* stop the lcdc */
base_addr_c = ch->dma_handle + ch->xres_virtual * ch->yres_virtual
+ c_offset;
- if (ch->cache)
- sh_mobile_meram_cache_update(priv->meram_dev, ch->cache,
- base_addr_y, base_addr_c,
- &base_addr_y, &base_addr_c);
-
ch->base_addr_y = base_addr_y;
ch->base_addr_c = base_addr_c;
ch->pan_y_offset = y_offset;
if (info->fbdefio) {
ch->sglist = vmalloc(sizeof(struct scatterlist) *
ch->fb_size >> PAGE_SHIFT);
- if (!ch->sglist) {
- dev_err(ch->lcdc->dev, "cannot allocate sglist\n");
+ if (!ch->sglist)
return -ENOMEM;
- }
}
info->bl_dev = ch->bl;
static int sh_mobile_lcdc_runtime_suspend(struct device *dev)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct sh_mobile_lcdc_priv *priv = platform_get_drvdata(pdev);
+ struct sh_mobile_lcdc_priv *priv = dev_get_drvdata(dev);
/* turn off LCDC hardware */
lcdc_write(priv, _LDCNT1R, 0);
static int sh_mobile_lcdc_runtime_resume(struct device *dev)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct sh_mobile_lcdc_priv *priv = platform_get_drvdata(pdev);
+ struct sh_mobile_lcdc_priv *priv = dev_get_drvdata(dev);
__sh_mobile_lcdc_start(priv);
}
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
- if (!priv) {
- dev_err(&pdev->dev, "cannot allocate device data\n");
+ if (!priv)
return -ENOMEM;
- }
priv->dev = &pdev->dev;
- priv->meram_dev = pdata->meram_dev;
+
for (i = 0; i < ARRAY_SIZE(priv->ch); i++)
mutex_init(&priv->ch[i].open_lock);
platform_set_drvdata(pdev, priv);
unsigned long *reg_offs;
unsigned long ldmt1r_value;
unsigned long enabled; /* ME and SE in LDCNT2R */
- void *cache;
struct mutex open_lock; /* protects the use counter */
int use_count;
+++ /dev/null
-/*
- * SuperH Mobile MERAM Driver for SuperH Mobile LCDC Driver
- *
- * Copyright (c) 2011 Damian Hobson-Garcia <dhobsong@igel.co.jp>
- * Takanari Hayama <taki@igel.co.jp>
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file "COPYING" in the main directory of this archive
- * for more details.
- */
-
-#include <linux/device.h>
-#include <linux/err.h>
-#include <linux/export.h>
-#include <linux/genalloc.h>
-#include <linux/io.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/pm_runtime.h>
-#include <linux/slab.h>
-
-#include <video/sh_mobile_meram.h>
-
-/* -----------------------------------------------------------------------------
- * MERAM registers
- */
-
-#define MEVCR1 0x4
-#define MEVCR1_RST (1 << 31)
-#define MEVCR1_WD (1 << 30)
-#define MEVCR1_AMD1 (1 << 29)
-#define MEVCR1_AMD0 (1 << 28)
-#define MEQSEL1 0x40
-#define MEQSEL2 0x44
-
-#define MExxCTL 0x400
-#define MExxCTL_BV (1 << 31)
-#define MExxCTL_BSZ_SHIFT 28
-#define MExxCTL_MSAR_MASK (0x7ff << MExxCTL_MSAR_SHIFT)
-#define MExxCTL_MSAR_SHIFT 16
-#define MExxCTL_NXT_MASK (0x1f << MExxCTL_NXT_SHIFT)
-#define MExxCTL_NXT_SHIFT 11
-#define MExxCTL_WD1 (1 << 10)
-#define MExxCTL_WD0 (1 << 9)
-#define MExxCTL_WS (1 << 8)
-#define MExxCTL_CB (1 << 7)
-#define MExxCTL_WBF (1 << 6)
-#define MExxCTL_WF (1 << 5)
-#define MExxCTL_RF (1 << 4)
-#define MExxCTL_CM (1 << 3)
-#define MExxCTL_MD_READ (1 << 0)
-#define MExxCTL_MD_WRITE (2 << 0)
-#define MExxCTL_MD_ICB_WB (3 << 0)
-#define MExxCTL_MD_ICB (4 << 0)
-#define MExxCTL_MD_FB (7 << 0)
-#define MExxCTL_MD_MASK (7 << 0)
-#define MExxBSIZE 0x404
-#define MExxBSIZE_RCNT_SHIFT 28
-#define MExxBSIZE_YSZM1_SHIFT 16
-#define MExxBSIZE_XSZM1_SHIFT 0
-#define MExxMNCF 0x408
-#define MExxMNCF_KWBNM_SHIFT 28
-#define MExxMNCF_KRBNM_SHIFT 24
-#define MExxMNCF_BNM_SHIFT 16
-#define MExxMNCF_XBV (1 << 15)
-#define MExxMNCF_CPL_YCBCR444 (1 << 12)
-#define MExxMNCF_CPL_YCBCR420 (2 << 12)
-#define MExxMNCF_CPL_YCBCR422 (3 << 12)
-#define MExxMNCF_CPL_MSK (3 << 12)
-#define MExxMNCF_BL (1 << 2)
-#define MExxMNCF_LNM_SHIFT 0
-#define MExxSARA 0x410
-#define MExxSARB 0x414
-#define MExxSBSIZE 0x418
-#define MExxSBSIZE_HDV (1 << 31)
-#define MExxSBSIZE_HSZ16 (0 << 28)
-#define MExxSBSIZE_HSZ32 (1 << 28)
-#define MExxSBSIZE_HSZ64 (2 << 28)
-#define MExxSBSIZE_HSZ128 (3 << 28)
-#define MExxSBSIZE_SBSIZZ_SHIFT 0
-
-#define MERAM_MExxCTL_VAL(next, addr) \
- ((((next) << MExxCTL_NXT_SHIFT) & MExxCTL_NXT_MASK) | \
- (((addr) << MExxCTL_MSAR_SHIFT) & MExxCTL_MSAR_MASK))
-#define MERAM_MExxBSIZE_VAL(rcnt, yszm1, xszm1) \
- (((rcnt) << MExxBSIZE_RCNT_SHIFT) | \
- ((yszm1) << MExxBSIZE_YSZM1_SHIFT) | \
- ((xszm1) << MExxBSIZE_XSZM1_SHIFT))
-
-static const unsigned long common_regs[] = {
- MEVCR1,
- MEQSEL1,
- MEQSEL2,
-};
-#define MERAM_REGS_SIZE ARRAY_SIZE(common_regs)
-
-static const unsigned long icb_regs[] = {
- MExxCTL,
- MExxBSIZE,
- MExxMNCF,
- MExxSARA,
- MExxSARB,
- MExxSBSIZE,
-};
-#define ICB_REGS_SIZE ARRAY_SIZE(icb_regs)
-
-/*
- * sh_mobile_meram_icb - MERAM ICB information
- * @regs: Registers cache
- * @index: ICB index
- * @offset: MERAM block offset
- * @size: MERAM block size in KiB
- * @cache_unit: Bytes to cache per ICB
- * @pixelformat: Video pixel format of the data stored in the ICB
- * @current_reg: Which of Start Address Register A (0) or B (1) is in use
- */
-struct sh_mobile_meram_icb {
- unsigned long regs[ICB_REGS_SIZE];
- unsigned int index;
- unsigned long offset;
- unsigned int size;
-
- unsigned int cache_unit;
- unsigned int pixelformat;
- unsigned int current_reg;
-};
-
-#define MERAM_ICB_NUM 32
-
-struct sh_mobile_meram_fb_plane {
- struct sh_mobile_meram_icb *marker;
- struct sh_mobile_meram_icb *cache;
-};
-
-struct sh_mobile_meram_fb_cache {
- unsigned int nplanes;
- struct sh_mobile_meram_fb_plane planes[2];
-};
-
-/*
- * sh_mobile_meram_priv - MERAM device
- * @base: Registers base address
- * @meram: MERAM physical address
- * @regs: Registers cache
- * @lock: Protects used_icb and icbs
- * @used_icb: Bitmask of used ICBs
- * @icbs: ICBs
- * @pool: Allocation pool to manage the MERAM
- */
-struct sh_mobile_meram_priv {
- void __iomem *base;
- unsigned long meram;
- unsigned long regs[MERAM_REGS_SIZE];
-
- struct mutex lock;
- unsigned long used_icb;
- struct sh_mobile_meram_icb icbs[MERAM_ICB_NUM];
-
- struct gen_pool *pool;
-};
-
-/* settings */
-#define MERAM_GRANULARITY 1024
-#define MERAM_SEC_LINE 15
-#define MERAM_LINE_WIDTH 2048
-
-/* -----------------------------------------------------------------------------
- * Registers access
- */
-
-#define MERAM_ICB_OFFSET(base, idx, off) ((base) + (off) + (idx) * 0x20)
-
-static inline void meram_write_icb(void __iomem *base, unsigned int idx,
- unsigned int off, unsigned long val)
-{
- iowrite32(val, MERAM_ICB_OFFSET(base, idx, off));
-}
-
-static inline unsigned long meram_read_icb(void __iomem *base, unsigned int idx,
- unsigned int off)
-{
- return ioread32(MERAM_ICB_OFFSET(base, idx, off));
-}
-
-static inline void meram_write_reg(void __iomem *base, unsigned int off,
- unsigned long val)
-{
- iowrite32(val, base + off);
-}
-
-static inline unsigned long meram_read_reg(void __iomem *base, unsigned int off)
-{
- return ioread32(base + off);
-}
-
-/* -----------------------------------------------------------------------------
- * MERAM allocation and free
- */
-
-static unsigned long meram_alloc(struct sh_mobile_meram_priv *priv, size_t size)
-{
- return gen_pool_alloc(priv->pool, size);
-}
-
-static void meram_free(struct sh_mobile_meram_priv *priv, unsigned long mem,
- size_t size)
-{
- gen_pool_free(priv->pool, mem, size);
-}
-
-/* -----------------------------------------------------------------------------
- * LCDC cache planes allocation, init, cleanup and free
- */
-
-/* Allocate ICBs and MERAM for a plane. */
-static int meram_plane_alloc(struct sh_mobile_meram_priv *priv,
- struct sh_mobile_meram_fb_plane *plane,
- size_t size)
-{
- unsigned long mem;
- unsigned long idx;
-
- idx = find_first_zero_bit(&priv->used_icb, 28);
- if (idx == 28)
- return -ENOMEM;
- plane->cache = &priv->icbs[idx];
-
- idx = find_next_zero_bit(&priv->used_icb, 32, 28);
- if (idx == 32)
- return -ENOMEM;
- plane->marker = &priv->icbs[idx];
-
- mem = meram_alloc(priv, size * 1024);
- if (mem == 0)
- return -ENOMEM;
-
- __set_bit(plane->marker->index, &priv->used_icb);
- __set_bit(plane->cache->index, &priv->used_icb);
-
- plane->marker->offset = mem - priv->meram;
- plane->marker->size = size;
-
- return 0;
-}
-
-/* Free ICBs and MERAM for a plane. */
-static void meram_plane_free(struct sh_mobile_meram_priv *priv,
- struct sh_mobile_meram_fb_plane *plane)
-{
- meram_free(priv, priv->meram + plane->marker->offset,
- plane->marker->size * 1024);
-
- __clear_bit(plane->marker->index, &priv->used_icb);
- __clear_bit(plane->cache->index, &priv->used_icb);
-}
-
-/* Is this a YCbCr(NV12, NV16 or NV24) colorspace? */
-static int is_nvcolor(int cspace)
-{
- if (cspace == SH_MOBILE_MERAM_PF_NV ||
- cspace == SH_MOBILE_MERAM_PF_NV24)
- return 1;
- return 0;
-}
-
-/* Set the next address to fetch. */
-static void meram_set_next_addr(struct sh_mobile_meram_priv *priv,
- struct sh_mobile_meram_fb_cache *cache,
- unsigned long base_addr_y,
- unsigned long base_addr_c)
-{
- struct sh_mobile_meram_icb *icb = cache->planes[0].marker;
- unsigned long target;
-
- icb->current_reg ^= 1;
- target = icb->current_reg ? MExxSARB : MExxSARA;
-
- /* set the next address to fetch */
- meram_write_icb(priv->base, cache->planes[0].cache->index, target,
- base_addr_y);
- meram_write_icb(priv->base, cache->planes[0].marker->index, target,
- base_addr_y + cache->planes[0].marker->cache_unit);
-
- if (cache->nplanes == 2) {
- meram_write_icb(priv->base, cache->planes[1].cache->index,
- target, base_addr_c);
- meram_write_icb(priv->base, cache->planes[1].marker->index,
- target, base_addr_c +
- cache->planes[1].marker->cache_unit);
- }
-}
-
-/* Get the next ICB address. */
-static void
-meram_get_next_icb_addr(struct sh_mobile_meram_info *pdata,
- struct sh_mobile_meram_fb_cache *cache,
- unsigned long *icb_addr_y, unsigned long *icb_addr_c)
-{
- struct sh_mobile_meram_icb *icb = cache->planes[0].marker;
- unsigned long icb_offset;
-
- if (pdata->addr_mode == SH_MOBILE_MERAM_MODE0)
- icb_offset = 0x80000000 | (icb->current_reg << 29);
- else
- icb_offset = 0xc0000000 | (icb->current_reg << 23);
-
- *icb_addr_y = icb_offset | (cache->planes[0].marker->index << 24);
- if (cache->nplanes == 2)
- *icb_addr_c = icb_offset
- | (cache->planes[1].marker->index << 24);
-}
-
-#define MERAM_CALC_BYTECOUNT(x, y) \
- (((x) * (y) + (MERAM_LINE_WIDTH - 1)) & ~(MERAM_LINE_WIDTH - 1))
-
-/* Initialize MERAM. */
-static int meram_plane_init(struct sh_mobile_meram_priv *priv,
- struct sh_mobile_meram_fb_plane *plane,
- unsigned int xres, unsigned int yres,
- unsigned int *out_pitch)
-{
- struct sh_mobile_meram_icb *marker = plane->marker;
- unsigned long total_byte_count = MERAM_CALC_BYTECOUNT(xres, yres);
- unsigned long bnm;
- unsigned int lcdc_pitch;
- unsigned int xpitch;
- unsigned int line_cnt;
- unsigned int save_lines;
-
- /* adjust pitch to 1024, 2048, 4096 or 8192 */
- lcdc_pitch = (xres - 1) | 1023;
- lcdc_pitch = lcdc_pitch | (lcdc_pitch >> 1);
- lcdc_pitch = lcdc_pitch | (lcdc_pitch >> 2);
- lcdc_pitch += 1;
-
- /* derive settings */
- if (lcdc_pitch == 8192 && yres >= 1024) {
- lcdc_pitch = xpitch = MERAM_LINE_WIDTH;
- line_cnt = total_byte_count >> 11;
- *out_pitch = xres;
- save_lines = plane->marker->size / 16 / MERAM_SEC_LINE;
- save_lines *= MERAM_SEC_LINE;
- } else {
- xpitch = xres;
- line_cnt = yres;
- *out_pitch = lcdc_pitch;
- save_lines = plane->marker->size / (lcdc_pitch >> 10) / 2;
- save_lines &= 0xff;
- }
- bnm = (save_lines - 1) << 16;
-
- /* TODO: we better to check if we have enough MERAM buffer size */
-
- /* set up ICB */
- meram_write_icb(priv->base, plane->cache->index, MExxBSIZE,
- MERAM_MExxBSIZE_VAL(0x0, line_cnt - 1, xpitch - 1));
- meram_write_icb(priv->base, plane->marker->index, MExxBSIZE,
- MERAM_MExxBSIZE_VAL(0xf, line_cnt - 1, xpitch - 1));
-
- meram_write_icb(priv->base, plane->cache->index, MExxMNCF, bnm);
- meram_write_icb(priv->base, plane->marker->index, MExxMNCF, bnm);
-
- meram_write_icb(priv->base, plane->cache->index, MExxSBSIZE, xpitch);
- meram_write_icb(priv->base, plane->marker->index, MExxSBSIZE, xpitch);
-
- /* save a cache unit size */
- plane->cache->cache_unit = xres * save_lines;
- plane->marker->cache_unit = xres * save_lines;
-
- /*
- * Set MERAM for framebuffer
- *
- * we also chain the cache_icb and the marker_icb.
- * we also split the allocated MERAM buffer between two ICBs.
- */
- meram_write_icb(priv->base, plane->cache->index, MExxCTL,
- MERAM_MExxCTL_VAL(plane->marker->index, marker->offset)
- | MExxCTL_WD1 | MExxCTL_WD0 | MExxCTL_WS | MExxCTL_CM |
- MExxCTL_MD_FB);
- meram_write_icb(priv->base, plane->marker->index, MExxCTL,
- MERAM_MExxCTL_VAL(plane->cache->index, marker->offset +
- plane->marker->size / 2) |
- MExxCTL_WD1 | MExxCTL_WD0 | MExxCTL_WS | MExxCTL_CM |
- MExxCTL_MD_FB);
-
- return 0;
-}
-
-static void meram_plane_cleanup(struct sh_mobile_meram_priv *priv,
- struct sh_mobile_meram_fb_plane *plane)
-{
- /* disable ICB */
- meram_write_icb(priv->base, plane->cache->index, MExxCTL,
- MExxCTL_WBF | MExxCTL_WF | MExxCTL_RF);
- meram_write_icb(priv->base, plane->marker->index, MExxCTL,
- MExxCTL_WBF | MExxCTL_WF | MExxCTL_RF);
-
- plane->cache->cache_unit = 0;
- plane->marker->cache_unit = 0;
-}
-
-/* -----------------------------------------------------------------------------
- * MERAM operations
- */
-
-unsigned long sh_mobile_meram_alloc(struct sh_mobile_meram_info *pdata,
- size_t size)
-{
- struct sh_mobile_meram_priv *priv = pdata->priv;
-
- return meram_alloc(priv, size);
-}
-EXPORT_SYMBOL_GPL(sh_mobile_meram_alloc);
-
-void sh_mobile_meram_free(struct sh_mobile_meram_info *pdata, unsigned long mem,
- size_t size)
-{
- struct sh_mobile_meram_priv *priv = pdata->priv;
-
- meram_free(priv, mem, size);
-}
-EXPORT_SYMBOL_GPL(sh_mobile_meram_free);
-
-/* Allocate memory for the ICBs and mark them as used. */
-static struct sh_mobile_meram_fb_cache *
-meram_cache_alloc(struct sh_mobile_meram_priv *priv,
- const struct sh_mobile_meram_cfg *cfg,
- int pixelformat)
-{
- unsigned int nplanes = is_nvcolor(pixelformat) ? 2 : 1;
- struct sh_mobile_meram_fb_cache *cache;
- int ret;
-
- cache = kzalloc(sizeof(*cache), GFP_KERNEL);
- if (cache == NULL)
- return ERR_PTR(-ENOMEM);
-
- cache->nplanes = nplanes;
-
- ret = meram_plane_alloc(priv, &cache->planes[0],
- cfg->icb[0].meram_size);
- if (ret < 0)
- goto error;
-
- cache->planes[0].marker->current_reg = 1;
- cache->planes[0].marker->pixelformat = pixelformat;
-
- if (cache->nplanes == 1)
- return cache;
-
- ret = meram_plane_alloc(priv, &cache->planes[1],
- cfg->icb[1].meram_size);
- if (ret < 0) {
- meram_plane_free(priv, &cache->planes[0]);
- goto error;
- }
-
- return cache;
-
-error:
- kfree(cache);
- return ERR_PTR(-ENOMEM);
-}
-
-void *sh_mobile_meram_cache_alloc(struct sh_mobile_meram_info *pdata,
- const struct sh_mobile_meram_cfg *cfg,
- unsigned int xres, unsigned int yres,
- unsigned int pixelformat, unsigned int *pitch)
-{
- struct sh_mobile_meram_fb_cache *cache;
- struct sh_mobile_meram_priv *priv = pdata->priv;
- struct platform_device *pdev = pdata->pdev;
- unsigned int nplanes = is_nvcolor(pixelformat) ? 2 : 1;
- unsigned int out_pitch;
-
- if (priv == NULL)
- return ERR_PTR(-ENODEV);
-
- if (pixelformat != SH_MOBILE_MERAM_PF_NV &&
- pixelformat != SH_MOBILE_MERAM_PF_NV24 &&
- pixelformat != SH_MOBILE_MERAM_PF_RGB)
- return ERR_PTR(-EINVAL);
-
- dev_dbg(&pdev->dev, "registering %dx%d (%s)", xres, yres,
- !pixelformat ? "yuv" : "rgb");
-
- /* we can't handle wider than 8192px */
- if (xres > 8192) {
- dev_err(&pdev->dev, "width exceeding the limit (> 8192).");
- return ERR_PTR(-EINVAL);
- }
-
- if (cfg->icb[0].meram_size == 0)
- return ERR_PTR(-EINVAL);
-
- if (nplanes == 2 && cfg->icb[1].meram_size == 0)
- return ERR_PTR(-EINVAL);
-
- mutex_lock(&priv->lock);
-
- /* We now register the ICBs and allocate the MERAM regions. */
- cache = meram_cache_alloc(priv, cfg, pixelformat);
- if (IS_ERR(cache)) {
- dev_err(&pdev->dev, "MERAM allocation failed (%ld).",
- PTR_ERR(cache));
- goto err;
- }
-
- /* initialize MERAM */
- meram_plane_init(priv, &cache->planes[0], xres, yres, &out_pitch);
- *pitch = out_pitch;
- if (pixelformat == SH_MOBILE_MERAM_PF_NV)
- meram_plane_init(priv, &cache->planes[1],
- xres, (yres + 1) / 2, &out_pitch);
- else if (pixelformat == SH_MOBILE_MERAM_PF_NV24)
- meram_plane_init(priv, &cache->planes[1],
- 2 * xres, (yres + 1) / 2, &out_pitch);
-
-err:
- mutex_unlock(&priv->lock);
- return cache;
-}
-EXPORT_SYMBOL_GPL(sh_mobile_meram_cache_alloc);
-
-void
-sh_mobile_meram_cache_free(struct sh_mobile_meram_info *pdata, void *data)
-{
- struct sh_mobile_meram_fb_cache *cache = data;
- struct sh_mobile_meram_priv *priv = pdata->priv;
-
- mutex_lock(&priv->lock);
-
- /* Cleanup and free. */
- meram_plane_cleanup(priv, &cache->planes[0]);
- meram_plane_free(priv, &cache->planes[0]);
-
- if (cache->nplanes == 2) {
- meram_plane_cleanup(priv, &cache->planes[1]);
- meram_plane_free(priv, &cache->planes[1]);
- }
-
- kfree(cache);
-
- mutex_unlock(&priv->lock);
-}
-EXPORT_SYMBOL_GPL(sh_mobile_meram_cache_free);
-
-void
-sh_mobile_meram_cache_update(struct sh_mobile_meram_info *pdata, void *data,
- unsigned long base_addr_y,
- unsigned long base_addr_c,
- unsigned long *icb_addr_y,
- unsigned long *icb_addr_c)
-{
- struct sh_mobile_meram_fb_cache *cache = data;
- struct sh_mobile_meram_priv *priv = pdata->priv;
-
- mutex_lock(&priv->lock);
-
- meram_set_next_addr(priv, cache, base_addr_y, base_addr_c);
- meram_get_next_icb_addr(pdata, cache, icb_addr_y, icb_addr_c);
-
- mutex_unlock(&priv->lock);
-}
-EXPORT_SYMBOL_GPL(sh_mobile_meram_cache_update);
-
-/* -----------------------------------------------------------------------------
- * Power management
- */
-
-#ifdef CONFIG_PM
-static int sh_mobile_meram_suspend(struct device *dev)
-{
- struct platform_device *pdev = to_platform_device(dev);
- struct sh_mobile_meram_priv *priv = platform_get_drvdata(pdev);
- unsigned int i, j;
-
- for (i = 0; i < MERAM_REGS_SIZE; i++)
- priv->regs[i] = meram_read_reg(priv->base, common_regs[i]);
-
- for (i = 0; i < 32; i++) {
- if (!test_bit(i, &priv->used_icb))
- continue;
- for (j = 0; j < ICB_REGS_SIZE; j++) {
- priv->icbs[i].regs[j] =
- meram_read_icb(priv->base, i, icb_regs[j]);
- /* Reset ICB on resume */
- if (icb_regs[j] == MExxCTL)
- priv->icbs[i].regs[j] |=
- MExxCTL_WBF | MExxCTL_WF | MExxCTL_RF;
- }
- }
- return 0;
-}
-
-static int sh_mobile_meram_resume(struct device *dev)
-{
- struct platform_device *pdev = to_platform_device(dev);
- struct sh_mobile_meram_priv *priv = platform_get_drvdata(pdev);
- unsigned int i, j;
-
- for (i = 0; i < 32; i++) {
- if (!test_bit(i, &priv->used_icb))
- continue;
- for (j = 0; j < ICB_REGS_SIZE; j++)
- meram_write_icb(priv->base, i, icb_regs[j],
- priv->icbs[i].regs[j]);
- }
-
- for (i = 0; i < MERAM_REGS_SIZE; i++)
- meram_write_reg(priv->base, common_regs[i], priv->regs[i]);
- return 0;
-}
-#endif /* CONFIG_PM */
-
-static UNIVERSAL_DEV_PM_OPS(sh_mobile_meram_dev_pm_ops,
- sh_mobile_meram_suspend,
- sh_mobile_meram_resume, NULL);
-
-/* -----------------------------------------------------------------------------
- * Probe/remove and driver init/exit
- */
-
-static int sh_mobile_meram_probe(struct platform_device *pdev)
-{
- struct sh_mobile_meram_priv *priv;
- struct sh_mobile_meram_info *pdata = pdev->dev.platform_data;
- struct resource *regs;
- struct resource *meram;
- unsigned int i;
- int error;
-
- if (!pdata) {
- dev_err(&pdev->dev, "no platform data defined\n");
- return -EINVAL;
- }
-
- regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- meram = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- if (regs == NULL || meram == NULL) {
- dev_err(&pdev->dev, "cannot get platform resources\n");
- return -ENOENT;
- }
-
- priv = kzalloc(sizeof(*priv), GFP_KERNEL);
- if (!priv) {
- dev_err(&pdev->dev, "cannot allocate device data\n");
- return -ENOMEM;
- }
-
- /* Initialize private data. */
- mutex_init(&priv->lock);
- priv->used_icb = pdata->reserved_icbs;
-
- for (i = 0; i < MERAM_ICB_NUM; ++i)
- priv->icbs[i].index = i;
-
- pdata->priv = priv;
- pdata->pdev = pdev;
-
- /* Request memory regions and remap the registers. */
- if (!request_mem_region(regs->start, resource_size(regs), pdev->name)) {
- dev_err(&pdev->dev, "MERAM registers region already claimed\n");
- error = -EBUSY;
- goto err_req_regs;
- }
-
- if (!request_mem_region(meram->start, resource_size(meram),
- pdev->name)) {
- dev_err(&pdev->dev, "MERAM memory region already claimed\n");
- error = -EBUSY;
- goto err_req_meram;
- }
-
- priv->base = ioremap_nocache(regs->start, resource_size(regs));
- if (!priv->base) {
- dev_err(&pdev->dev, "ioremap failed\n");
- error = -EFAULT;
- goto err_ioremap;
- }
-
- priv->meram = meram->start;
-
- /* Create and initialize the MERAM memory pool. */
- priv->pool = gen_pool_create(ilog2(MERAM_GRANULARITY), -1);
- if (priv->pool == NULL) {
- error = -ENOMEM;
- goto err_genpool;
- }
-
- error = gen_pool_add(priv->pool, meram->start, resource_size(meram),
- -1);
- if (error < 0)
- goto err_genpool;
-
- /* initialize ICB addressing mode */
- if (pdata->addr_mode == SH_MOBILE_MERAM_MODE1)
- meram_write_reg(priv->base, MEVCR1, MEVCR1_AMD1);
-
- platform_set_drvdata(pdev, priv);
- pm_runtime_enable(&pdev->dev);
-
- dev_info(&pdev->dev, "sh_mobile_meram initialized.");
-
- return 0;
-
-err_genpool:
- if (priv->pool)
- gen_pool_destroy(priv->pool);
- iounmap(priv->base);
-err_ioremap:
- release_mem_region(meram->start, resource_size(meram));
-err_req_meram:
- release_mem_region(regs->start, resource_size(regs));
-err_req_regs:
- mutex_destroy(&priv->lock);
- kfree(priv);
-
- return error;
-}
-
-
-static int sh_mobile_meram_remove(struct platform_device *pdev)
-{
- struct sh_mobile_meram_priv *priv = platform_get_drvdata(pdev);
- struct resource *regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- struct resource *meram = platform_get_resource(pdev, IORESOURCE_MEM, 1);
-
- pm_runtime_disable(&pdev->dev);
-
- gen_pool_destroy(priv->pool);
-
- iounmap(priv->base);
- release_mem_region(meram->start, resource_size(meram));
- release_mem_region(regs->start, resource_size(regs));
-
- mutex_destroy(&priv->lock);
-
- kfree(priv);
-
- return 0;
-}
-
-static struct platform_driver sh_mobile_meram_driver = {
- .driver = {
- .name = "sh_mobile_meram",
- .pm = &sh_mobile_meram_dev_pm_ops,
- },
- .probe = sh_mobile_meram_probe,
- .remove = sh_mobile_meram_remove,
-};
-
-module_platform_driver(sh_mobile_meram_driver);
-
-MODULE_DESCRIPTION("SuperH Mobile MERAM driver");
-MODULE_AUTHOR("Damian Hobson-Garcia / Takanari Hayama");
-MODULE_LICENSE("GPL v2");
* @dev: PCI device
* @msg: the suspend event code.
*
- * See Documentation/power/admin-guide/devices.rst for more information
+ * See Documentation/driver-api/pm/devices.rst for more information
*/
static int xxxfb_suspend(struct pci_dev *dev, pm_message_t msg)
{
* xxxfb_resume - Optional but recommended function. Resume the device.
* @dev: PCI device
*
- * See Documentation/power/admin-guide/devices.rst for more information
+ * See Documentation/driver-api/pm/devices.rst for more information
*/
static int xxxfb_resume(struct pci_dev *dev)
{
* @dev: platform device
* @msg: the suspend event code.
*
- * See Documentation/power/admin-guide/devices.rst for more information
+ * See Documentation/driver-api/pm/devices.rst for more information
*/
static int xxxfb_suspend(struct platform_device *dev, pm_message_t msg)
{
* xxxfb_resume - Optional but recommended function. Resume the device.
* @dev: platform device
*
- * See Documentation/power/admin-guide/devices.rst for more information
+ * See Documentation/driver-api/pm/devices.rst for more information
*/
static int xxxfb_resume(struct platform_dev *dev)
{
int ret;
/* allocate our framebuffers */
-
- info = kzalloc(sizeof(struct sm501fb_info), GFP_KERNEL);
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
if (!info) {
dev_err(dev, "failed to allocate state\n");
return -ENOMEM;
#include <linux/console.h>
#include <linux/timer.h>
+#ifdef CONFIG_X86
+#include <asm/olpc.h>
+#else
+#define machine_is_olpc(x) 0
+#endif
+
#include "debug.h"
#include "viafbdev.h"
*/
#include <linux/via-core.h>
-#include <asm/olpc.h>
#include "global.h"
#include "via_clock.h"
#include <linux/platform_device.h>
#include <linux/list.h>
#include <linux/pm.h>
-#include <asm/olpc.h>
/*
* The default port config.
#include <linux/kernel.h>
#include <linux/via-core.h>
-#include <asm/olpc.h>
+
#include "via_clock.h"
#include "global.h"
#include "debug.h"
#include <linux/stat.h>
#include <linux/via-core.h>
#include <linux/via_i2c.h>
-#include <asm/olpc.h>
#define _MASTER_FILE
#include "global.h"
struct virtio_pci_device *vp_dev = pci_get_drvdata(pci_dev);
struct device *dev = get_device(&vp_dev->vdev.dev);
+ pci_disable_sriov(pci_dev);
+
unregister_virtio_device(&vp_dev->vdev);
if (vp_dev->ioaddr)
put_device(dev);
}
+static int virtio_pci_sriov_configure(struct pci_dev *pci_dev, int num_vfs)
+{
+ struct virtio_pci_device *vp_dev = pci_get_drvdata(pci_dev);
+ struct virtio_device *vdev = &vp_dev->vdev;
+ int ret;
+
+ if (!(vdev->config->get_status(vdev) & VIRTIO_CONFIG_S_DRIVER_OK))
+ return -EBUSY;
+
+ if (!__virtio_test_bit(vdev, VIRTIO_F_SR_IOV))
+ return -EINVAL;
+
+ if (pci_vfs_assigned(pci_dev))
+ return -EPERM;
+
+ if (num_vfs == 0) {
+ pci_disable_sriov(pci_dev);
+ return 0;
+ }
+
+ ret = pci_enable_sriov(pci_dev, num_vfs);
+ if (ret < 0)
+ return ret;
+
+ return num_vfs;
+}
+
static struct pci_driver virtio_pci_driver = {
.name = "virtio-pci",
.id_table = virtio_pci_id_table,
#ifdef CONFIG_PM_SLEEP
.driver.pm = &virtio_pci_pm_ops,
#endif
+ .sriov_configure = virtio_pci_sriov_configure,
};
module_pci_driver(virtio_pci_driver);
return features;
}
+static void vp_transport_features(struct virtio_device *vdev, u64 features)
+{
+ struct virtio_pci_device *vp_dev = to_vp_device(vdev);
+ struct pci_dev *pci_dev = vp_dev->pci_dev;
+
+ if ((features & BIT_ULL(VIRTIO_F_SR_IOV)) &&
+ pci_find_ext_capability(pci_dev, PCI_EXT_CAP_ID_SRIOV))
+ __virtio_set_bit(vdev, VIRTIO_F_SR_IOV);
+}
+
/* virtio config->finalize_features() implementation */
static int vp_finalize_features(struct virtio_device *vdev)
{
struct virtio_pci_device *vp_dev = to_vp_device(vdev);
+ u64 features = vdev->features;
/* Give virtio_ring a chance to accept features. */
vring_transport_features(vdev);
+ /* Give virtio_pci a chance to accept features. */
+ vp_transport_features(vdev, features);
+
if (!__virtio_test_bit(vdev, VIRTIO_F_VERSION_1)) {
dev_err(&vdev->dev, "virtio: device uses modern interface "
"but does not have VIRTIO_F_VERSION_1\n");
will automatically feed it to the correct interpreter.
You can do other nice things, too. Read the file
- <file:Documentation/binfmt_misc.txt> to learn how to use this
+ <file:Documentation/admin-guide/binfmt-misc.rst> to learn how to use this
feature, <file:Documentation/admin-guide/java.rst> for information about how
to include Java support. and <file:Documentation/admin-guide/mono.rst> for
information about how to include Mono-based .NET support.
return;
cur_time:
- *tv = current_time(inode);
+ *tv = timespec64_to_timespec(current_time(inode));
return;
too_early:
struct inode *
adfs_iget(struct super_block *sb, struct object_info *obj)
{
+ struct timespec ts;
struct inode *inode;
inode = new_inode(sb);
ADFS_I(inode)->stamped = ((obj->loadaddr & 0xfff00000) == 0xfff00000);
inode->i_mode = adfs_atts2mode(sb, inode);
- adfs_adfs2unix_time(&inode->i_mtime, inode);
+ ts = timespec64_to_timespec(inode->i_mtime);
+ adfs_adfs2unix_time(&ts, inode);
+ inode->i_mtime = timespec_to_timespec64(ts);
inode->i_atime = inode->i_mtime;
inode->i_ctime = inode->i_mtime;
afs-cache-$(CONFIG_AFS_FSCACHE) := cache.o
-kafs-objs := \
+kafs-y := \
$(afs-cache-y) \
addr_list.o \
callback.o \
main.o \
misc.o \
mntpt.o \
- proc.o \
rotate.o \
rxrpc.o \
security.o \
write.o \
xattr.o
+kafs-$(CONFIG_PROC_FS) += proc.o
obj-$(CONFIG_AFS_FS) := kafs.o
_enter("%s", cell->name);
ret = dns_query("afsdb", cell->name, cell->name_len,
- "ipv4", &vllist, _expiry);
+ "", &vllist, _expiry);
if (ret < 0)
return ERR_PTR(ret);
#include <linux/sched.h>
#include "internal.h"
+/*
+ * Create volume and callback interests on a server.
+ */
+static struct afs_cb_interest *afs_create_interest(struct afs_server *server,
+ struct afs_vnode *vnode)
+{
+ struct afs_vol_interest *new_vi, *vi;
+ struct afs_cb_interest *new;
+ struct hlist_node **pp;
+
+ new_vi = kzalloc(sizeof(struct afs_vol_interest), GFP_KERNEL);
+ if (!new_vi)
+ return NULL;
+
+ new = kzalloc(sizeof(struct afs_cb_interest), GFP_KERNEL);
+ if (!new) {
+ kfree(new_vi);
+ return NULL;
+ }
+
+ new_vi->usage = 1;
+ new_vi->vid = vnode->volume->vid;
+ INIT_HLIST_NODE(&new_vi->srv_link);
+ INIT_HLIST_HEAD(&new_vi->cb_interests);
+
+ refcount_set(&new->usage, 1);
+ new->sb = vnode->vfs_inode.i_sb;
+ new->vid = vnode->volume->vid;
+ new->server = afs_get_server(server);
+ INIT_HLIST_NODE(&new->cb_vlink);
+
+ write_lock(&server->cb_break_lock);
+
+ for (pp = &server->cb_volumes.first; *pp; pp = &(*pp)->next) {
+ vi = hlist_entry(*pp, struct afs_vol_interest, srv_link);
+ if (vi->vid < new_vi->vid)
+ continue;
+ if (vi->vid > new_vi->vid)
+ break;
+ vi->usage++;
+ goto found_vi;
+ }
+
+ new_vi->srv_link.pprev = pp;
+ new_vi->srv_link.next = *pp;
+ if (*pp)
+ (*pp)->pprev = &new_vi->srv_link.next;
+ *pp = &new_vi->srv_link;
+ vi = new_vi;
+ new_vi = NULL;
+found_vi:
+
+ new->vol_interest = vi;
+ hlist_add_head(&new->cb_vlink, &vi->cb_interests);
+
+ write_unlock(&server->cb_break_lock);
+ kfree(new_vi);
+ return new;
+}
+
/*
* Set up an interest-in-callbacks record for a volume on a server and
* register it with the server.
}
if (!cbi) {
- new = kzalloc(sizeof(struct afs_cb_interest), GFP_KERNEL);
+ new = afs_create_interest(server, vnode);
if (!new)
return -ENOMEM;
- refcount_set(&new->usage, 1);
- new->sb = vnode->vfs_inode.i_sb;
- new->vid = vnode->volume->vid;
- new->server = afs_get_server(server);
- INIT_LIST_HEAD(&new->cb_link);
-
- write_lock(&server->cb_break_lock);
- list_add_tail(&new->cb_link, &server->cb_interests);
- write_unlock(&server->cb_break_lock);
-
write_lock(&slist->lock);
if (!entry->cb_interest) {
entry->cb_interest = afs_get_cb_interest(new);
*/
void afs_put_cb_interest(struct afs_net *net, struct afs_cb_interest *cbi)
{
+ struct afs_vol_interest *vi;
+
if (cbi && refcount_dec_and_test(&cbi->usage)) {
- if (!list_empty(&cbi->cb_link)) {
+ if (!hlist_unhashed(&cbi->cb_vlink)) {
write_lock(&cbi->server->cb_break_lock);
- list_del_init(&cbi->cb_link);
+
+ hlist_del_init(&cbi->cb_vlink);
+ vi = cbi->vol_interest;
+ cbi->vol_interest = NULL;
+ if (--vi->usage == 0)
+ hlist_del(&vi->srv_link);
+ else
+ vi = NULL;
+
write_unlock(&cbi->server->cb_break_lock);
+ kfree(vi);
afs_put_server(net, cbi->server);
}
kfree(cbi);
static void afs_break_one_callback(struct afs_server *server,
struct afs_fid *fid)
{
+ struct afs_vol_interest *vi;
struct afs_cb_interest *cbi;
struct afs_iget_data data;
struct afs_vnode *vnode;
struct inode *inode;
read_lock(&server->cb_break_lock);
+ hlist_for_each_entry(vi, &server->cb_volumes, srv_link) {
+ if (vi->vid < fid->vid)
+ continue;
+ if (vi->vid > fid->vid) {
+ vi = NULL;
+ break;
+ }
+ //atomic_inc(&vi->usage);
+ break;
+ }
+
+ /* TODO: Find all matching volumes if we couldn't match the server and
+ * break them anyway.
+ */
+ if (!vi)
+ goto out;
/* Step through all interested superblocks. There may be more than one
* because of cell aliasing.
*/
- list_for_each_entry(cbi, &server->cb_interests, cb_link) {
- if (cbi->vid != fid->vid)
- continue;
-
+ hlist_for_each_entry(cbi, &vi->cb_interests, cb_vlink) {
if (fid->vnode == 0 && fid->unique == 0) {
/* The callback break applies to an entire volume. */
struct afs_super_info *as = AFS_FS_S(cbi->sb);
}
}
+out:
read_unlock(&server->cb_break_lock);
}
#include <linux/dns_resolver.h>
#include <linux/sched.h>
#include <linux/inet.h>
+#include <linux/namei.h>
#include <keys/rxrpc-type.h>
#include "internal.h"
/* install the new cell */
write_seqlock(&net->cells_lock);
- old_root = net->ws_cell;
- net->ws_cell = new_root;
+ old_root = rcu_access_pointer(net->ws_cell);
+ rcu_assign_pointer(net->ws_cell, new_root);
write_sequnlock(&net->cells_lock);
afs_put_cell(net, old_root);
NULL, 0,
cell, 0, true);
#endif
- ret = afs_proc_cell_setup(net, cell);
+ ret = afs_proc_cell_setup(cell);
if (ret < 0)
return ret;
- spin_lock(&net->proc_cells_lock);
+
+ mutex_lock(&net->proc_cells_lock);
list_add_tail(&cell->proc_link, &net->proc_cells);
- spin_unlock(&net->proc_cells_lock);
+ afs_dynroot_mkdir(net, cell);
+ mutex_unlock(&net->proc_cells_lock);
return 0;
}
{
_enter("%s", cell->name);
- afs_proc_cell_remove(net, cell);
+ afs_proc_cell_remove(cell);
- spin_lock(&net->proc_cells_lock);
+ mutex_lock(&net->proc_cells_lock);
list_del_init(&cell->proc_link);
- spin_unlock(&net->proc_cells_lock);
+ afs_dynroot_rmdir(net, cell);
+ mutex_unlock(&net->proc_cells_lock);
#ifdef CONFIG_AFS_FSCACHE
fscache_relinquish_cookie(cell->cache, NULL, false);
_enter("");
write_seqlock(&net->cells_lock);
- ws = net->ws_cell;
- net->ws_cell = NULL;
+ ws = rcu_access_pointer(net->ws_cell);
+ RCU_INIT_POINTER(net->ws_cell, NULL);
write_sequnlock(&net->cells_lock);
afs_put_cell(net, ws);
nifs = 0;
ifs = kcalloc(32, sizeof(*ifs), GFP_KERNEL);
if (ifs) {
- nifs = afs_get_ipv4_interfaces(ifs, 32, false);
+ nifs = afs_get_ipv4_interfaces(call->net, ifs, 32, false);
if (nifs < 0) {
kfree(ifs);
ifs = NULL;
-/* dir.c: AFS dynamic root handling
+/* AFS dynamic root handling
*
* Copyright (C) 2018 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
return 0;
}
- ret = dns_query("afsdb", name, len, "ipv4", NULL, NULL);
+ ret = dns_query("afsdb", name, len, "", NULL, NULL);
if (ret == -ENODATA)
ret = -EDESTADDRREQ;
return ret;
.d_release = afs_d_release,
.d_automount = afs_d_automount,
};
+
+/*
+ * Create a manually added cell mount directory.
+ * - The caller must hold net->proc_cells_lock
+ */
+int afs_dynroot_mkdir(struct afs_net *net, struct afs_cell *cell)
+{
+ struct super_block *sb = net->dynroot_sb;
+ struct dentry *root, *subdir;
+ int ret;
+
+ if (!sb || atomic_read(&sb->s_active) == 0)
+ return 0;
+
+ /* Let the ->lookup op do the creation */
+ root = sb->s_root;
+ inode_lock(root->d_inode);
+ subdir = lookup_one_len(cell->name, root, cell->name_len);
+ if (IS_ERR(subdir)) {
+ ret = PTR_ERR(subdir);
+ goto unlock;
+ }
+
+ /* Note that we're retaining an extra ref on the dentry */
+ subdir->d_fsdata = (void *)1UL;
+ ret = 0;
+unlock:
+ inode_unlock(root->d_inode);
+ return ret;
+}
+
+/*
+ * Remove a manually added cell mount directory.
+ * - The caller must hold net->proc_cells_lock
+ */
+void afs_dynroot_rmdir(struct afs_net *net, struct afs_cell *cell)
+{
+ struct super_block *sb = net->dynroot_sb;
+ struct dentry *root, *subdir;
+
+ if (!sb || atomic_read(&sb->s_active) == 0)
+ return;
+
+ root = sb->s_root;
+ inode_lock(root->d_inode);
+
+ /* Don't want to trigger a lookup call, which will re-add the cell */
+ subdir = try_lookup_one_len(cell->name, root, cell->name_len);
+ if (IS_ERR_OR_NULL(subdir)) {
+ _debug("lookup %ld", PTR_ERR(subdir));
+ goto no_dentry;
+ }
+
+ _debug("rmdir %pd %u", subdir, d_count(subdir));
+
+ if (subdir->d_fsdata) {
+ _debug("unpin %u", d_count(subdir));
+ subdir->d_fsdata = NULL;
+ dput(subdir);
+ }
+ dput(subdir);
+no_dentry:
+ inode_unlock(root->d_inode);
+ _leave("");
+}
+
+/*
+ * Populate a newly created dynamic root with cell names.
+ */
+int afs_dynroot_populate(struct super_block *sb)
+{
+ struct afs_cell *cell;
+ struct afs_net *net = afs_sb2net(sb);
+ int ret;
+
+ if (mutex_lock_interruptible(&net->proc_cells_lock) < 0)
+ return -ERESTARTSYS;
+
+ net->dynroot_sb = sb;
+ list_for_each_entry(cell, &net->proc_cells, proc_link) {
+ ret = afs_dynroot_mkdir(net, cell);
+ if (ret < 0)
+ goto error;
+ }
+
+ ret = 0;
+out:
+ mutex_unlock(&net->proc_cells_lock);
+ return ret;
+
+error:
+ net->dynroot_sb = NULL;
+ goto out;
+}
+
+/*
+ * When a dynamic root that's in the process of being destroyed, depopulate it
+ * of pinned directories.
+ */
+void afs_dynroot_depopulate(struct super_block *sb)
+{
+ struct afs_net *net = afs_sb2net(sb);
+ struct dentry *root = sb->s_root, *subdir, *tmp;
+
+ /* Prevent more subdirs from being created */
+ mutex_lock(&net->proc_cells_lock);
+ if (net->dynroot_sb == sb)
+ net->dynroot_sb = NULL;
+ mutex_unlock(&net->proc_cells_lock);
+
+ inode_lock(root->d_inode);
+
+ /* Remove all the pins for dirs created for manually added cells */
+ list_for_each_entry_safe(subdir, tmp, &root->d_subdirs, d_child) {
+ if (subdir->d_fsdata) {
+ subdir->d_fsdata = NULL;
+ dput(subdir);
+ }
+ }
+
+ inode_unlock(root->d_inode);
+}
const afs_dataversion_t *expected_version,
u8 flags)
{
- struct timespec t;
+ struct timespec64 t;
umode_t mode;
t.tv_sec = status->mtime_client;
u64 data_version, size;
u32 type, abort_code;
u8 flags = 0;
- int ret;
-
- if (vnode)
- write_seqlock(&vnode->cb_lock);
abort_code = ntohl(xdr->abort_code);
* case.
*/
status->abort_code = abort_code;
- ret = 0;
- goto out;
+ return 0;
}
pr_warn("Unknown AFSFetchStatus version %u\n", ntohl(xdr->if_version));
if (abort_code != 0 && inline_error) {
status->abort_code = abort_code;
- ret = 0;
- goto out;
+ return 0;
}
type = ntohl(xdr->type);
flags);
}
- ret = 0;
-
-out:
- if (vnode)
- write_sequnlock(&vnode->cb_lock);
- return ret;
+ return 0;
bad:
xdr_dump_bad(*_bp);
- ret = afs_protocol_error(call, -EBADMSG);
- goto out;
+ return afs_protocol_error(call, -EBADMSG);
+}
+
+/*
+ * Decode the file status. We need to lock the target vnode if we're going to
+ * update its status so that stat() sees the attributes update atomically.
+ */
+static int afs_decode_status(struct afs_call *call,
+ const __be32 **_bp,
+ struct afs_file_status *status,
+ struct afs_vnode *vnode,
+ const afs_dataversion_t *expected_version,
+ struct afs_read *read_req)
+{
+ int ret;
+
+ if (!vnode)
+ return xdr_decode_AFSFetchStatus(call, _bp, status, vnode,
+ expected_version, read_req);
+
+ write_seqlock(&vnode->cb_lock);
+ ret = xdr_decode_AFSFetchStatus(call, _bp, status, vnode,
+ expected_version, read_req);
+ write_sequnlock(&vnode->cb_lock);
+ return ret;
}
/*
/* unmarshall the reply once we've received all of it */
bp = call->buffer;
- if (xdr_decode_AFSFetchStatus(call, &bp, &vnode->status, vnode,
- &call->expected_version, NULL) < 0)
+ if (afs_decode_status(call, &bp, &vnode->status, vnode,
+ &call->expected_version, NULL) < 0)
return afs_protocol_error(call, -EBADMSG);
xdr_decode_AFSCallBack(call, vnode, &bp);
if (call->reply[1])
return ret;
bp = call->buffer;
- if (xdr_decode_AFSFetchStatus(call, &bp, &vnode->status, vnode,
- &vnode->status.data_version, req) < 0)
+ if (afs_decode_status(call, &bp, &vnode->status, vnode,
+ &vnode->status.data_version, req) < 0)
return afs_protocol_error(call, -EBADMSG);
xdr_decode_AFSCallBack(call, vnode, &bp);
if (call->reply[1])
/* unmarshall the reply once we've received all of it */
bp = call->buffer;
xdr_decode_AFSFid(&bp, call->reply[1]);
- if (xdr_decode_AFSFetchStatus(call, &bp, call->reply[2], NULL, NULL, NULL) < 0 ||
- xdr_decode_AFSFetchStatus(call, &bp, &vnode->status, vnode,
- &call->expected_version, NULL) < 0)
+ if (afs_decode_status(call, &bp, call->reply[2], NULL, NULL, NULL) < 0 ||
+ afs_decode_status(call, &bp, &vnode->status, vnode,
+ &call->expected_version, NULL) < 0)
return afs_protocol_error(call, -EBADMSG);
xdr_decode_AFSCallBack_raw(&bp, call->reply[3]);
/* xdr_decode_AFSVolSync(&bp, call->reply[X]); */
/* unmarshall the reply once we've received all of it */
bp = call->buffer;
- if (xdr_decode_AFSFetchStatus(call, &bp, &vnode->status, vnode,
- &call->expected_version, NULL) < 0)
+ if (afs_decode_status(call, &bp, &vnode->status, vnode,
+ &call->expected_version, NULL) < 0)
return afs_protocol_error(call, -EBADMSG);
/* xdr_decode_AFSVolSync(&bp, call->reply[X]); */
/* unmarshall the reply once we've received all of it */
bp = call->buffer;
- if (xdr_decode_AFSFetchStatus(call, &bp, &vnode->status, vnode, NULL, NULL) < 0 ||
- xdr_decode_AFSFetchStatus(call, &bp, &dvnode->status, dvnode,
- &call->expected_version, NULL) < 0)
+ if (afs_decode_status(call, &bp, &vnode->status, vnode, NULL, NULL) < 0 ||
+ afs_decode_status(call, &bp, &dvnode->status, dvnode,
+ &call->expected_version, NULL) < 0)
return afs_protocol_error(call, -EBADMSG);
/* xdr_decode_AFSVolSync(&bp, call->reply[X]); */
/* unmarshall the reply once we've received all of it */
bp = call->buffer;
xdr_decode_AFSFid(&bp, call->reply[1]);
- if (xdr_decode_AFSFetchStatus(call, &bp, call->reply[2], NULL, NULL, NULL) ||
- xdr_decode_AFSFetchStatus(call, &bp, &vnode->status, vnode,
- &call->expected_version, NULL) < 0)
+ if (afs_decode_status(call, &bp, call->reply[2], NULL, NULL, NULL) ||
+ afs_decode_status(call, &bp, &vnode->status, vnode,
+ &call->expected_version, NULL) < 0)
return afs_protocol_error(call, -EBADMSG);
/* xdr_decode_AFSVolSync(&bp, call->reply[X]); */
/* unmarshall the reply once we've received all of it */
bp = call->buffer;
- if (xdr_decode_AFSFetchStatus(call, &bp, &orig_dvnode->status, orig_dvnode,
- &call->expected_version, NULL) < 0)
+ if (afs_decode_status(call, &bp, &orig_dvnode->status, orig_dvnode,
+ &call->expected_version, NULL) < 0)
return afs_protocol_error(call, -EBADMSG);
if (new_dvnode != orig_dvnode &&
- xdr_decode_AFSFetchStatus(call, &bp, &new_dvnode->status, new_dvnode,
- &call->expected_version_2, NULL) < 0)
+ afs_decode_status(call, &bp, &new_dvnode->status, new_dvnode,
+ &call->expected_version_2, NULL) < 0)
return afs_protocol_error(call, -EBADMSG);
/* xdr_decode_AFSVolSync(&bp, call->reply[X]); */
/* unmarshall the reply once we've received all of it */
bp = call->buffer;
- if (xdr_decode_AFSFetchStatus(call, &bp, &vnode->status, vnode,
- &call->expected_version, NULL) < 0)
+ if (afs_decode_status(call, &bp, &vnode->status, vnode,
+ &call->expected_version, NULL) < 0)
return afs_protocol_error(call, -EBADMSG);
/* xdr_decode_AFSVolSync(&bp, call->reply[X]); */
/* unmarshall the reply once we've received all of it */
bp = call->buffer;
- if (xdr_decode_AFSFetchStatus(call, &bp, &vnode->status, vnode,
- &call->expected_version, NULL) < 0)
+ if (afs_decode_status(call, &bp, &vnode->status, vnode,
+ &call->expected_version, NULL) < 0)
return afs_protocol_error(call, -EBADMSG);
/* xdr_decode_AFSVolSync(&bp, call->reply[X]); */
/* unmarshall the reply once we've received all of it */
bp = call->buffer;
- xdr_decode_AFSFetchStatus(call, &bp, status, vnode,
- &call->expected_version, NULL);
+ afs_decode_status(call, &bp, status, vnode,
+ &call->expected_version, NULL);
callback[call->count].version = ntohl(bp[0]);
callback[call->count].expiry = ntohl(bp[1]);
callback[call->count].type = ntohl(bp[2]);
bp = call->buffer;
statuses = call->reply[1];
- if (xdr_decode_AFSFetchStatus(call, &bp, &statuses[call->count],
- call->count == 0 ? vnode : NULL,
- NULL, NULL) < 0)
+ if (afs_decode_status(call, &bp, &statuses[call->count],
+ call->count == 0 ? vnode : NULL,
+ NULL, NULL) < 0)
return afs_protocol_error(call, -EBADMSG);
call->count++;
#include <linux/backing-dev.h>
#include <linux/uuid.h>
#include <net/net_namespace.h>
+#include <net/netns/generic.h>
+#include <net/sock.h>
#include <net/af_rxrpc.h>
#include "afs.h"
afs_voltype_t type; /* type of volume requested */
int volnamesz; /* size of volume name */
const char *volname; /* name of volume to mount */
- struct afs_net *net; /* Network namespace in effect */
+ struct net *net_ns; /* Network namespace in effect */
+ struct afs_net *net; /* the AFS net namespace stuff */
struct afs_cell *cell; /* cell in which to find volume */
struct afs_volume *volume; /* volume record */
struct key *key; /* key to use for secure mounting */
* - there's one superblock per volume
*/
struct afs_super_info {
- struct afs_net *net; /* Network namespace */
+ struct net *net_ns; /* Network namespace */
struct afs_cell *cell; /* The cell in which the volume resides */
struct afs_volume *volume; /* volume record */
bool dyn_root; /* True if dynamic root */
char *subs[AFS_NR_SYSNAME];
refcount_t usage;
unsigned short nr;
- short error;
char blank[1];
};
* AFS network namespace record.
*/
struct afs_net {
+ struct net *net; /* Backpointer to the owning net namespace */
struct afs_uuid uuid;
bool live; /* F if this namespace is being removed */
/* Cell database */
struct rb_root cells;
- struct afs_cell *ws_cell;
+ struct afs_cell __rcu *ws_cell;
struct work_struct cells_manager;
struct timer_list cells_timer;
atomic_t cells_outstanding;
seqlock_t cells_lock;
- spinlock_t proc_cells_lock;
+ struct mutex proc_cells_lock;
struct list_head proc_cells;
/* Known servers. Theoretically each fileserver can only be in one
struct mutex lock_manager_mutex;
/* Misc */
+ struct super_block *dynroot_sb; /* Dynamic root mount superblock */
struct proc_dir_entry *proc_afs; /* /proc/net/afs directory */
struct afs_sysnames *sysnames;
rwlock_t sysnames_lock;
};
extern const char afs_init_sysname[];
-extern struct afs_net __afs_net;// Dummy AFS network namespace; TODO: replace with real netns
enum afs_cell_state {
AFS_CELL_UNSET,
rwlock_t fs_lock; /* access lock */
/* callback promise management */
- struct list_head cb_interests; /* List of superblocks using this server */
+ struct hlist_head cb_volumes; /* List of volume interests on this server */
unsigned cb_s_break; /* Break-everything counter. */
rwlock_t cb_break_lock; /* Volume finding lock */
};
+/*
+ * Volume collation in the server's callback interest list.
+ */
+struct afs_vol_interest {
+ struct hlist_node srv_link; /* Link in server->cb_volumes */
+ struct hlist_head cb_interests; /* List of callback interests on the server */
+ afs_volid_t vid; /* Volume ID to match */
+ unsigned int usage;
+};
+
/*
* Interest by a superblock on a server.
*/
struct afs_cb_interest {
- struct list_head cb_link; /* Link in server->cb_interests */
+ struct hlist_node cb_vlink; /* Link in vol_interest->cb_interests */
+ struct afs_vol_interest *vol_interest;
struct afs_server *server; /* Server on which this interest resides */
struct super_block *sb; /* Superblock on which inodes reside */
afs_volid_t vid; /* Volume ID to match */
extern const struct dentry_operations afs_dynroot_dentry_operations;
extern struct inode *afs_try_auto_mntpt(struct dentry *, struct inode *);
+extern int afs_dynroot_mkdir(struct afs_net *, struct afs_cell *);
+extern void afs_dynroot_rmdir(struct afs_net *, struct afs_cell *);
+extern int afs_dynroot_populate(struct super_block *);
+extern void afs_dynroot_depopulate(struct super_block *);
/*
* file.c
* main.c
*/
extern struct workqueue_struct *afs_wq;
+extern int afs_net_id;
-static inline struct afs_net *afs_d2net(struct dentry *dentry)
+static inline struct afs_net *afs_net(struct net *net)
{
- return &__afs_net;
+ return net_generic(net, afs_net_id);
}
-static inline struct afs_net *afs_i2net(struct inode *inode)
+static inline struct afs_net *afs_sb2net(struct super_block *sb)
{
- return &__afs_net;
+ return afs_net(AFS_FS_S(sb)->net_ns);
}
-static inline struct afs_net *afs_v2net(struct afs_vnode *vnode)
+static inline struct afs_net *afs_d2net(struct dentry *dentry)
{
- return &__afs_net;
+ return afs_sb2net(dentry->d_sb);
}
-static inline struct afs_net *afs_sock2net(struct sock *sk)
+static inline struct afs_net *afs_i2net(struct inode *inode)
{
- return &__afs_net;
+ return afs_sb2net(inode->i_sb);
}
-static inline struct afs_net *afs_get_net(struct afs_net *net)
+static inline struct afs_net *afs_v2net(struct afs_vnode *vnode)
{
- return net;
+ return afs_i2net(&vnode->vfs_inode);
}
-static inline void afs_put_net(struct afs_net *net)
+static inline struct afs_net *afs_sock2net(struct sock *sk)
{
+ return net_generic(sock_net(sk), afs_net_id);
}
static inline void __afs_stat(atomic_t *s)
/*
* netdevices.c
*/
-extern int afs_get_ipv4_interfaces(struct afs_interface *, size_t, bool);
+extern int afs_get_ipv4_interfaces(struct afs_net *, struct afs_interface *,
+ size_t, bool);
/*
* proc.c
*/
+#ifdef CONFIG_PROC_FS
extern int __net_init afs_proc_init(struct afs_net *);
extern void __net_exit afs_proc_cleanup(struct afs_net *);
-extern int afs_proc_cell_setup(struct afs_net *, struct afs_cell *);
-extern void afs_proc_cell_remove(struct afs_net *, struct afs_cell *);
+extern int afs_proc_cell_setup(struct afs_cell *);
+extern void afs_proc_cell_remove(struct afs_cell *);
extern void afs_put_sysnames(struct afs_sysnames *);
+#else
+static inline int afs_proc_init(struct afs_net *net) { return 0; }
+static inline void afs_proc_cleanup(struct afs_net *net) {}
+static inline int afs_proc_cell_setup(struct afs_cell *cell) { return 0; }
+static inline void afs_proc_cell_remove(struct afs_cell *cell) {}
+static inline void afs_put_sysnames(struct afs_sysnames *sysnames) {}
+#endif
/*
* rotate.c
* super.c
*/
extern int __init afs_fs_init(void);
-extern void __exit afs_fs_exit(void);
+extern void afs_fs_exit(void);
/*
* vlclient.c
#include <linux/completion.h>
#include <linux/sched.h>
#include <linux/random.h>
+#include <linux/proc_fs.h>
#define CREATE_TRACE_POINTS
#include "internal.h"
MODULE_PARM_DESC(rootcell, "root AFS cell name and VL server IP addr list");
struct workqueue_struct *afs_wq;
-struct afs_net __afs_net;
+static struct proc_dir_entry *afs_proc_symlink;
#if defined(CONFIG_ALPHA)
const char afs_init_sysname[] = "alpha_linux26";
/*
* Initialise an AFS network namespace record.
*/
-static int __net_init afs_net_init(struct afs_net *net)
+static int __net_init afs_net_init(struct net *net_ns)
{
struct afs_sysnames *sysnames;
+ struct afs_net *net = afs_net(net_ns);
int ret;
+ net->net = net_ns;
net->live = true;
generate_random_uuid((unsigned char *)&net->uuid);
INIT_WORK(&net->cells_manager, afs_manage_cells);
timer_setup(&net->cells_timer, afs_cells_timer, 0);
- spin_lock_init(&net->proc_cells_lock);
+ mutex_init(&net->proc_cells_lock);
INIT_LIST_HEAD(&net->proc_cells);
seqlock_init(&net->fs_lock);
/*
* Clean up and destroy an AFS network namespace record.
*/
-static void __net_exit afs_net_exit(struct afs_net *net)
+static void __net_exit afs_net_exit(struct net *net_ns)
{
+ struct afs_net *net = afs_net(net_ns);
+
net->live = false;
afs_cell_purge(net);
afs_purge_servers(net);
afs_put_sysnames(net->sysnames);
}
+static struct pernet_operations afs_net_ops = {
+ .init = afs_net_init,
+ .exit = afs_net_exit,
+ .id = &afs_net_id,
+ .size = sizeof(struct afs_net),
+};
+
/*
* initialise the AFS client FS module
*/
goto error_cache;
#endif
- ret = afs_net_init(&__afs_net);
+ ret = register_pernet_subsys(&afs_net_ops);
if (ret < 0)
goto error_net;
if (ret < 0)
goto error_fs;
+ afs_proc_symlink = proc_symlink("fs/afs", NULL, "../self/net/afs");
+ if (IS_ERR(afs_proc_symlink)) {
+ ret = PTR_ERR(afs_proc_symlink);
+ goto error_proc;
+ }
+
return ret;
+error_proc:
+ afs_fs_exit();
error_fs:
- afs_net_exit(&__afs_net);
+ unregister_pernet_subsys(&afs_net_ops);
error_net:
#ifdef CONFIG_AFS_FSCACHE
fscache_unregister_netfs(&afs_cache_netfs);
{
printk(KERN_INFO "kAFS: Red Hat AFS client v0.1 unregistering.\n");
+ proc_remove(afs_proc_symlink);
afs_fs_exit();
- afs_net_exit(&__afs_net);
+ unregister_pernet_subsys(&afs_net_ops);
#ifdef CONFIG_AFS_FSCACHE
fscache_unregister_netfs(&afs_cache_netfs);
#endif
* - maxbufs must be at least 1
* - returns the number of interface records in the buffer
*/
-int afs_get_ipv4_interfaces(struct afs_interface *bufs, size_t maxbufs,
- bool wantloopback)
+int afs_get_ipv4_interfaces(struct afs_net *net, struct afs_interface *bufs,
+ size_t maxbufs, bool wantloopback)
{
struct net_device *dev;
struct in_device *idev;
ASSERT(maxbufs > 0);
rtnl_lock();
- for_each_netdev(&init_net, dev) {
+ for_each_netdev(net->net, dev) {
if (dev->type == ARPHRD_LOOPBACK && !wantloopback)
continue;
idev = __in_dev_get_rtnl(dev);
#include <linux/uaccess.h>
#include "internal.h"
-static inline struct afs_net *afs_proc2net(struct file *f)
+static inline struct afs_net *afs_seq2net(struct seq_file *m)
{
- return &__afs_net;
+ return afs_net(seq_file_net(m));
}
-static inline struct afs_net *afs_seq2net(struct seq_file *m)
+static inline struct afs_net *afs_seq2net_single(struct seq_file *m)
{
- return &__afs_net; // TODO: use seq_file_net(m)
+ return afs_net(seq_file_single_net(m));
}
-static int afs_proc_cells_open(struct inode *inode, struct file *file);
-static void *afs_proc_cells_start(struct seq_file *p, loff_t *pos);
-static void *afs_proc_cells_next(struct seq_file *p, void *v, loff_t *pos);
-static void afs_proc_cells_stop(struct seq_file *p, void *v);
-static int afs_proc_cells_show(struct seq_file *m, void *v);
-static ssize_t afs_proc_cells_write(struct file *file, const char __user *buf,
- size_t size, loff_t *_pos);
-
-static const struct seq_operations afs_proc_cells_ops = {
- .start = afs_proc_cells_start,
- .next = afs_proc_cells_next,
- .stop = afs_proc_cells_stop,
- .show = afs_proc_cells_show,
-};
-
-static const struct file_operations afs_proc_cells_fops = {
- .open = afs_proc_cells_open,
- .read = seq_read,
- .write = afs_proc_cells_write,
- .llseek = seq_lseek,
- .release = seq_release,
-};
-
-static ssize_t afs_proc_rootcell_read(struct file *file, char __user *buf,
- size_t size, loff_t *_pos);
-static ssize_t afs_proc_rootcell_write(struct file *file,
- const char __user *buf,
- size_t size, loff_t *_pos);
-
-static const struct file_operations afs_proc_rootcell_fops = {
- .read = afs_proc_rootcell_read,
- .write = afs_proc_rootcell_write,
- .llseek = no_llseek,
-};
-
-static void *afs_proc_cell_volumes_start(struct seq_file *p, loff_t *pos);
-static void *afs_proc_cell_volumes_next(struct seq_file *p, void *v,
- loff_t *pos);
-static void afs_proc_cell_volumes_stop(struct seq_file *p, void *v);
-static int afs_proc_cell_volumes_show(struct seq_file *m, void *v);
-
-static const struct seq_operations afs_proc_cell_volumes_ops = {
- .start = afs_proc_cell_volumes_start,
- .next = afs_proc_cell_volumes_next,
- .stop = afs_proc_cell_volumes_stop,
- .show = afs_proc_cell_volumes_show,
-};
-
-static void *afs_proc_cell_vlservers_start(struct seq_file *p, loff_t *pos);
-static void *afs_proc_cell_vlservers_next(struct seq_file *p, void *v,
- loff_t *pos);
-static void afs_proc_cell_vlservers_stop(struct seq_file *p, void *v);
-static int afs_proc_cell_vlservers_show(struct seq_file *m, void *v);
-
-static const struct seq_operations afs_proc_cell_vlservers_ops = {
- .start = afs_proc_cell_vlservers_start,
- .next = afs_proc_cell_vlservers_next,
- .stop = afs_proc_cell_vlservers_stop,
- .show = afs_proc_cell_vlservers_show,
-};
-
-static void *afs_proc_servers_start(struct seq_file *p, loff_t *pos);
-static void *afs_proc_servers_next(struct seq_file *p, void *v,
- loff_t *pos);
-static void afs_proc_servers_stop(struct seq_file *p, void *v);
-static int afs_proc_servers_show(struct seq_file *m, void *v);
-
-static const struct seq_operations afs_proc_servers_ops = {
- .start = afs_proc_servers_start,
- .next = afs_proc_servers_next,
- .stop = afs_proc_servers_stop,
- .show = afs_proc_servers_show,
-};
-
-static int afs_proc_sysname_open(struct inode *inode, struct file *file);
-static int afs_proc_sysname_release(struct inode *inode, struct file *file);
-static void *afs_proc_sysname_start(struct seq_file *p, loff_t *pos);
-static void *afs_proc_sysname_next(struct seq_file *p, void *v,
- loff_t *pos);
-static void afs_proc_sysname_stop(struct seq_file *p, void *v);
-static int afs_proc_sysname_show(struct seq_file *m, void *v);
-static ssize_t afs_proc_sysname_write(struct file *file,
- const char __user *buf,
- size_t size, loff_t *_pos);
-
-static const struct seq_operations afs_proc_sysname_ops = {
- .start = afs_proc_sysname_start,
- .next = afs_proc_sysname_next,
- .stop = afs_proc_sysname_stop,
- .show = afs_proc_sysname_show,
-};
-
-static const struct file_operations afs_proc_sysname_fops = {
- .open = afs_proc_sysname_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = afs_proc_sysname_release,
- .write = afs_proc_sysname_write,
-};
-
-static int afs_proc_stats_show(struct seq_file *m, void *v);
-
/*
- * initialise the /proc/fs/afs/ directory
+ * Display the list of cells known to the namespace.
*/
-int afs_proc_init(struct afs_net *net)
+static int afs_proc_cells_show(struct seq_file *m, void *v)
{
- _enter("");
-
- net->proc_afs = proc_mkdir("fs/afs", NULL);
- if (!net->proc_afs)
- goto error_dir;
+ struct afs_cell *cell = list_entry(v, struct afs_cell, proc_link);
+ struct afs_net *net = afs_seq2net(m);
- if (!proc_create("cells", 0644, net->proc_afs, &afs_proc_cells_fops) ||
- !proc_create("rootcell", 0644, net->proc_afs, &afs_proc_rootcell_fops) ||
- !proc_create_seq("servers", 0644, net->proc_afs, &afs_proc_servers_ops) ||
- !proc_create_single("stats", 0644, net->proc_afs, afs_proc_stats_show) ||
- !proc_create("sysname", 0644, net->proc_afs, &afs_proc_sysname_fops))
- goto error_tree;
+ if (v == &net->proc_cells) {
+ /* display header on line 1 */
+ seq_puts(m, "USE NAME\n");
+ return 0;
+ }
- _leave(" = 0");
+ /* display one cell per line on subsequent lines */
+ seq_printf(m, "%3u %s\n", atomic_read(&cell->usage), cell->name);
return 0;
-
-error_tree:
- proc_remove(net->proc_afs);
-error_dir:
- _leave(" = -ENOMEM");
- return -ENOMEM;
-}
-
-/*
- * clean up the /proc/fs/afs/ directory
- */
-void afs_proc_cleanup(struct afs_net *net)
-{
- proc_remove(net->proc_afs);
- net->proc_afs = NULL;
-}
-
-/*
- * open "/proc/fs/afs/cells" which provides a summary of extant cells
- */
-static int afs_proc_cells_open(struct inode *inode, struct file *file)
-{
- return seq_open(file, &afs_proc_cells_ops);
}
-/*
- * set up the iterator to start reading from the cells list and return the
- * first item
- */
static void *afs_proc_cells_start(struct seq_file *m, loff_t *_pos)
__acquires(rcu)
{
- struct afs_net *net = afs_seq2net(m);
-
rcu_read_lock();
- return seq_list_start_head(&net->proc_cells, *_pos);
+ return seq_list_start_head(&afs_seq2net(m)->proc_cells, *_pos);
}
-/*
- * move to next cell in cells list
- */
static void *afs_proc_cells_next(struct seq_file *m, void *v, loff_t *pos)
{
- struct afs_net *net = afs_seq2net(m);
-
- return seq_list_next(v, &net->proc_cells, pos);
+ return seq_list_next(v, &afs_seq2net(m)->proc_cells, pos);
}
-/*
- * clean up after reading from the cells list
- */
static void afs_proc_cells_stop(struct seq_file *m, void *v)
__releases(rcu)
{
rcu_read_unlock();
}
-/*
- * display a header line followed by a load of cell lines
- */
-static int afs_proc_cells_show(struct seq_file *m, void *v)
-{
- struct afs_cell *cell = list_entry(v, struct afs_cell, proc_link);
- struct afs_net *net = afs_seq2net(m);
-
- if (v == &net->proc_cells) {
- /* display header on line 1 */
- seq_puts(m, "USE NAME\n");
- return 0;
- }
-
- /* display one cell per line on subsequent lines */
- seq_printf(m, "%3u %s\n", atomic_read(&cell->usage), cell->name);
- return 0;
-}
+static const struct seq_operations afs_proc_cells_ops = {
+ .start = afs_proc_cells_start,
+ .next = afs_proc_cells_next,
+ .stop = afs_proc_cells_stop,
+ .show = afs_proc_cells_show,
+};
/*
* handle writes to /proc/fs/afs/cells
* - to add cells: echo "add <cellname> <IP>[:<IP>][:<IP>]"
*/
-static ssize_t afs_proc_cells_write(struct file *file, const char __user *buf,
- size_t size, loff_t *_pos)
+static int afs_proc_cells_write(struct file *file, char *buf, size_t size)
{
- struct afs_net *net = afs_proc2net(file);
- char *kbuf, *name, *args;
+ struct seq_file *m = file->private_data;
+ struct afs_net *net = afs_seq2net(m);
+ char *name, *args;
int ret;
- /* start by dragging the command into memory */
- if (size <= 1 || size >= PAGE_SIZE)
- return -EINVAL;
-
- kbuf = memdup_user_nul(buf, size);
- if (IS_ERR(kbuf))
- return PTR_ERR(kbuf);
-
/* trim to first NL */
- name = memchr(kbuf, '\n', size);
+ name = memchr(buf, '\n', size);
if (name)
*name = 0;
/* split into command, name and argslist */
- name = strchr(kbuf, ' ');
+ name = strchr(buf, ' ');
if (!name)
goto inval;
do {
goto inval;
/* determine command to perform */
- _debug("cmd=%s name=%s args=%s", kbuf, name, args);
+ _debug("cmd=%s name=%s args=%s", buf, name, args);
- if (strcmp(kbuf, "add") == 0) {
+ if (strcmp(buf, "add") == 0) {
struct afs_cell *cell;
cell = afs_lookup_cell(net, name, strlen(name), args, true);
goto inval;
}
- ret = size;
+ ret = 0;
done:
- kfree(kbuf);
_leave(" = %d", ret);
return ret;
goto done;
}
-static ssize_t afs_proc_rootcell_read(struct file *file, char __user *buf,
- size_t size, loff_t *_pos)
+/*
+ * Display the name of the current workstation cell.
+ */
+static int afs_proc_rootcell_show(struct seq_file *m, void *v)
{
struct afs_cell *cell;
- struct afs_net *net = afs_proc2net(file);
- unsigned int seq = 0;
- char name[AFS_MAXCELLNAME + 1];
- int len;
-
- if (*_pos > 0)
- return 0;
- if (!net->ws_cell)
- return 0;
-
- rcu_read_lock();
- do {
- read_seqbegin_or_lock(&net->cells_lock, &seq);
- len = 0;
- cell = rcu_dereference_raw(net->ws_cell);
- if (cell) {
- len = cell->name_len;
- memcpy(name, cell->name, len);
- }
- } while (need_seqretry(&net->cells_lock, seq));
- done_seqretry(&net->cells_lock, seq);
- rcu_read_unlock();
-
- if (!len)
- return 0;
-
- name[len++] = '\n';
- if (len > size)
- len = size;
- if (copy_to_user(buf, name, len) != 0)
- return -EFAULT;
- *_pos = 1;
- return len;
+ struct afs_net *net;
+
+ net = afs_seq2net_single(m);
+ if (rcu_access_pointer(net->ws_cell)) {
+ rcu_read_lock();
+ cell = rcu_dereference(net->ws_cell);
+ if (cell)
+ seq_printf(m, "%s\n", cell->name);
+ rcu_read_unlock();
+ }
+ return 0;
}
/*
- * handle writes to /proc/fs/afs/rootcell
- * - to initialize rootcell: echo "cell.name:192.168.231.14"
+ * Set the current workstation cell and optionally supply its list of volume
+ * location servers.
+ *
+ * echo "cell.name:192.168.231.14" >/proc/fs/afs/rootcell
*/
-static ssize_t afs_proc_rootcell_write(struct file *file,
- const char __user *buf,
- size_t size, loff_t *_pos)
+static int afs_proc_rootcell_write(struct file *file, char *buf, size_t size)
{
- struct afs_net *net = afs_proc2net(file);
- char *kbuf, *s;
+ struct seq_file *m = file->private_data;
+ struct afs_net *net = afs_seq2net_single(m);
+ char *s;
int ret;
- /* start by dragging the command into memory */
- if (size <= 1 || size >= PAGE_SIZE)
- return -EINVAL;
-
- kbuf = memdup_user_nul(buf, size);
- if (IS_ERR(kbuf))
- return PTR_ERR(kbuf);
-
ret = -EINVAL;
- if (kbuf[0] == '.')
+ if (buf[0] == '.')
goto out;
- if (memchr(kbuf, '/', size))
+ if (memchr(buf, '/', size))
goto out;
/* trim to first NL */
- s = memchr(kbuf, '\n', size);
+ s = memchr(buf, '\n', size);
if (s)
*s = 0;
/* determine command to perform */
- _debug("rootcell=%s", kbuf);
+ _debug("rootcell=%s", buf);
- ret = afs_cell_init(net, kbuf);
- if (ret >= 0)
- ret = size; /* consume everything, always */
+ ret = afs_cell_init(net, buf);
out:
- kfree(kbuf);
_leave(" = %d", ret);
return ret;
}
+static const char afs_vol_types[3][3] = {
+ [AFSVL_RWVOL] = "RW",
+ [AFSVL_ROVOL] = "RO",
+ [AFSVL_BACKVOL] = "BK",
+};
+
/*
- * initialise /proc/fs/afs/<cell>/
+ * Display the list of volumes known to a cell.
*/
-int afs_proc_cell_setup(struct afs_net *net, struct afs_cell *cell)
+static int afs_proc_cell_volumes_show(struct seq_file *m, void *v)
{
- struct proc_dir_entry *dir;
-
- _enter("%p{%s},%p", cell, cell->name, net->proc_afs);
+ struct afs_cell *cell = PDE_DATA(file_inode(m->file));
+ struct afs_volume *vol = list_entry(v, struct afs_volume, proc_link);
- dir = proc_mkdir(cell->name, net->proc_afs);
- if (!dir)
- goto error_dir;
+ /* Display header on line 1 */
+ if (v == &cell->proc_volumes) {
+ seq_puts(m, "USE VID TY\n");
+ return 0;
+ }
- if (!proc_create_seq_data("vlservers", 0, dir,
- &afs_proc_cell_vlservers_ops, cell))
- goto error_tree;
- if (!proc_create_seq_data("volumes", 0, dir, &afs_proc_cell_volumes_ops,
- cell))
- goto error_tree;
+ seq_printf(m, "%3d %08x %s\n",
+ atomic_read(&vol->usage), vol->vid,
+ afs_vol_types[vol->type]);
- _leave(" = 0");
return 0;
-
-error_tree:
- remove_proc_subtree(cell->name, net->proc_afs);
-error_dir:
- _leave(" = -ENOMEM");
- return -ENOMEM;
}
-/*
- * remove /proc/fs/afs/<cell>/
- */
-void afs_proc_cell_remove(struct afs_net *net, struct afs_cell *cell)
-{
- _enter("");
-
- remove_proc_subtree(cell->name, net->proc_afs);
-
- _leave("");
-}
-
-/*
- * set up the iterator to start reading from the cells list and return the
- * first item
- */
static void *afs_proc_cell_volumes_start(struct seq_file *m, loff_t *_pos)
__acquires(cell->proc_lock)
{
struct afs_cell *cell = PDE_DATA(file_inode(m->file));
- _enter("cell=%p pos=%Ld", cell, *_pos);
-
read_lock(&cell->proc_lock);
return seq_list_start_head(&cell->proc_volumes, *_pos);
}
-/*
- * move to next cell in cells list
- */
-static void *afs_proc_cell_volumes_next(struct seq_file *p, void *v,
+static void *afs_proc_cell_volumes_next(struct seq_file *m, void *v,
loff_t *_pos)
{
- struct afs_cell *cell = PDE_DATA(file_inode(p->file));
+ struct afs_cell *cell = PDE_DATA(file_inode(m->file));
- _enter("cell=%p pos=%Ld", cell, *_pos);
return seq_list_next(v, &cell->proc_volumes, _pos);
}
-/*
- * clean up after reading from the cells list
- */
-static void afs_proc_cell_volumes_stop(struct seq_file *p, void *v)
+static void afs_proc_cell_volumes_stop(struct seq_file *m, void *v)
__releases(cell->proc_lock)
{
- struct afs_cell *cell = PDE_DATA(file_inode(p->file));
+ struct afs_cell *cell = PDE_DATA(file_inode(m->file));
read_unlock(&cell->proc_lock);
}
-static const char afs_vol_types[3][3] = {
- [AFSVL_RWVOL] = "RW",
- [AFSVL_ROVOL] = "RO",
- [AFSVL_BACKVOL] = "BK",
+static const struct seq_operations afs_proc_cell_volumes_ops = {
+ .start = afs_proc_cell_volumes_start,
+ .next = afs_proc_cell_volumes_next,
+ .stop = afs_proc_cell_volumes_stop,
+ .show = afs_proc_cell_volumes_show,
};
/*
- * display a header line followed by a load of volume lines
+ * Display the list of Volume Location servers we're using for a cell.
*/
-static int afs_proc_cell_volumes_show(struct seq_file *m, void *v)
+static int afs_proc_cell_vlservers_show(struct seq_file *m, void *v)
{
- struct afs_cell *cell = PDE_DATA(file_inode(m->file));
- struct afs_volume *vol = list_entry(v, struct afs_volume, proc_link);
+ struct sockaddr_rxrpc *addr = v;
- /* Display header on line 1 */
- if (v == &cell->proc_volumes) {
- seq_puts(m, "USE VID TY\n");
+ /* display header on line 1 */
+ if (v == (void *)1) {
+ seq_puts(m, "ADDRESS\n");
return 0;
}
- seq_printf(m, "%3d %08x %s\n",
- atomic_read(&vol->usage), vol->vid,
- afs_vol_types[vol->type]);
-
+ /* display one cell per line on subsequent lines */
+ seq_printf(m, "%pISp\n", &addr->transport);
return 0;
}
-/*
- * set up the iterator to start reading from the cells list and return the
- * first item
- */
static void *afs_proc_cell_vlservers_start(struct seq_file *m, loff_t *_pos)
__acquires(rcu)
{
return alist->addrs + pos;
}
-/*
- * move to next cell in cells list
- */
-static void *afs_proc_cell_vlservers_next(struct seq_file *p, void *v,
+static void *afs_proc_cell_vlservers_next(struct seq_file *m, void *v,
loff_t *_pos)
{
struct afs_addr_list *alist;
- struct afs_cell *cell = PDE_DATA(file_inode(p->file));
+ struct afs_cell *cell = PDE_DATA(file_inode(m->file));
loff_t pos;
alist = rcu_dereference(cell->vl_addrs);
return alist->addrs + pos;
}
-/*
- * clean up after reading from the cells list
- */
-static void afs_proc_cell_vlservers_stop(struct seq_file *p, void *v)
+static void afs_proc_cell_vlservers_stop(struct seq_file *m, void *v)
__releases(rcu)
{
rcu_read_unlock();
}
+static const struct seq_operations afs_proc_cell_vlservers_ops = {
+ .start = afs_proc_cell_vlservers_start,
+ .next = afs_proc_cell_vlservers_next,
+ .stop = afs_proc_cell_vlservers_stop,
+ .show = afs_proc_cell_vlservers_show,
+};
+
/*
- * display a header line followed by a load of volume lines
+ * Display the list of fileservers we're using within a namespace.
*/
-static int afs_proc_cell_vlservers_show(struct seq_file *m, void *v)
+static int afs_proc_servers_show(struct seq_file *m, void *v)
{
- struct sockaddr_rxrpc *addr = v;
+ struct afs_server *server;
+ struct afs_addr_list *alist;
+ int i;
- /* display header on line 1 */
- if (v == (void *)1) {
- seq_puts(m, "ADDRESS\n");
+ if (v == SEQ_START_TOKEN) {
+ seq_puts(m, "UUID USE ADDR\n");
return 0;
}
- /* display one cell per line on subsequent lines */
- seq_printf(m, "%pISp\n", &addr->transport);
+ server = list_entry(v, struct afs_server, proc_link);
+ alist = rcu_dereference(server->addresses);
+ seq_printf(m, "%pU %3d %pISpc%s\n",
+ &server->uuid,
+ atomic_read(&server->usage),
+ &alist->addrs[0].transport,
+ alist->index == 0 ? "*" : "");
+ for (i = 1; i < alist->nr_addrs; i++)
+ seq_printf(m, " %pISpc%s\n",
+ &alist->addrs[i].transport,
+ alist->index == i ? "*" : "");
return 0;
}
-/*
- * Set up the iterator to start reading from the server list and return the
- * first item.
- */
static void *afs_proc_servers_start(struct seq_file *m, loff_t *_pos)
__acquires(rcu)
{
- struct afs_net *net = afs_seq2net(m);
-
rcu_read_lock();
- return seq_hlist_start_head_rcu(&net->fs_proc, *_pos);
+ return seq_hlist_start_head_rcu(&afs_seq2net(m)->fs_proc, *_pos);
}
-/*
- * move to next cell in cells list
- */
static void *afs_proc_servers_next(struct seq_file *m, void *v, loff_t *_pos)
{
- struct afs_net *net = afs_seq2net(m);
-
- return seq_hlist_next_rcu(v, &net->fs_proc, _pos);
+ return seq_hlist_next_rcu(v, &afs_seq2net(m)->fs_proc, _pos);
}
-/*
- * clean up after reading from the cells list
- */
-static void afs_proc_servers_stop(struct seq_file *p, void *v)
+static void afs_proc_servers_stop(struct seq_file *m, void *v)
__releases(rcu)
{
rcu_read_unlock();
}
+static const struct seq_operations afs_proc_servers_ops = {
+ .start = afs_proc_servers_start,
+ .next = afs_proc_servers_next,
+ .stop = afs_proc_servers_stop,
+ .show = afs_proc_servers_show,
+};
+
/*
- * display a header line followed by a load of volume lines
+ * Display the list of strings that may be substituted for the @sys pathname
+ * macro.
*/
-static int afs_proc_servers_show(struct seq_file *m, void *v)
+static int afs_proc_sysname_show(struct seq_file *m, void *v)
{
- struct afs_server *server;
- struct afs_addr_list *alist;
-
- if (v == SEQ_START_TOKEN) {
- seq_puts(m, "UUID USE ADDR\n");
- return 0;
- }
+ struct afs_net *net = afs_seq2net(m);
+ struct afs_sysnames *sysnames = net->sysnames;
+ unsigned int i = (unsigned long)v - 1;
- server = list_entry(v, struct afs_server, proc_link);
- alist = rcu_dereference(server->addresses);
- seq_printf(m, "%pU %3d %pISp\n",
- &server->uuid,
- atomic_read(&server->usage),
- &alist->addrs[alist->index].transport);
+ if (i < sysnames->nr)
+ seq_printf(m, "%s\n", sysnames->subs[i]);
return 0;
}
-void afs_put_sysnames(struct afs_sysnames *sysnames)
+static void *afs_proc_sysname_start(struct seq_file *m, loff_t *pos)
+ __acquires(&net->sysnames_lock)
{
- int i;
+ struct afs_net *net = afs_seq2net(m);
+ struct afs_sysnames *names;
- if (sysnames && refcount_dec_and_test(&sysnames->usage)) {
- for (i = 0; i < sysnames->nr; i++)
- if (sysnames->subs[i] != afs_init_sysname &&
- sysnames->subs[i] != sysnames->blank)
- kfree(sysnames->subs[i]);
- }
+ read_lock(&net->sysnames_lock);
+
+ names = net->sysnames;
+ if (*pos >= names->nr)
+ return NULL;
+ return (void *)(unsigned long)(*pos + 1);
}
-/*
- * Handle opening of /proc/fs/afs/sysname. If it is opened for writing, we
- * assume the caller wants to change the substitution list and we allocate a
- * buffer to hold the list.
- */
-static int afs_proc_sysname_open(struct inode *inode, struct file *file)
+static void *afs_proc_sysname_next(struct seq_file *m, void *v, loff_t *pos)
{
- struct afs_sysnames *sysnames;
- struct seq_file *m;
- int ret;
-
- ret = seq_open(file, &afs_proc_sysname_ops);
- if (ret < 0)
- return ret;
+ struct afs_net *net = afs_seq2net(m);
+ struct afs_sysnames *names = net->sysnames;
- if (file->f_mode & FMODE_WRITE) {
- sysnames = kzalloc(sizeof(*sysnames), GFP_KERNEL);
- if (!sysnames) {
- seq_release(inode, file);
- return -ENOMEM;
- }
+ *pos += 1;
+ if (*pos >= names->nr)
+ return NULL;
+ return (void *)(unsigned long)(*pos + 1);
+}
- refcount_set(&sysnames->usage, 1);
- m = file->private_data;
- m->private = sysnames;
- }
+static void afs_proc_sysname_stop(struct seq_file *m, void *v)
+ __releases(&net->sysnames_lock)
+{
+ struct afs_net *net = afs_seq2net(m);
- return 0;
+ read_unlock(&net->sysnames_lock);
}
+static const struct seq_operations afs_proc_sysname_ops = {
+ .start = afs_proc_sysname_start,
+ .next = afs_proc_sysname_next,
+ .stop = afs_proc_sysname_stop,
+ .show = afs_proc_sysname_show,
+};
+
/*
- * Handle writes to /proc/fs/afs/sysname to set the @sys substitution.
+ * Allow the @sys substitution to be configured.
*/
-static ssize_t afs_proc_sysname_write(struct file *file,
- const char __user *buf,
- size_t size, loff_t *_pos)
+static int afs_proc_sysname_write(struct file *file, char *buf, size_t size)
{
- struct afs_sysnames *sysnames;
+ struct afs_sysnames *sysnames, *kill;
struct seq_file *m = file->private_data;
- char *kbuf = NULL, *s, *p, *sub;
+ struct afs_net *net = afs_seq2net(m);
+ char *s, *p, *sub;
int ret, len;
- sysnames = m->private;
+ sysnames = kzalloc(sizeof(*sysnames), GFP_KERNEL);
if (!sysnames)
- return -EINVAL;
- if (sysnames->error)
- return sysnames->error;
-
- if (size >= PAGE_SIZE - 1) {
- sysnames->error = -EINVAL;
- return -EINVAL;
- }
- if (size == 0)
- return 0;
-
- kbuf = memdup_user_nul(buf, size);
- if (IS_ERR(kbuf))
- return PTR_ERR(kbuf);
-
- inode_lock(file_inode(file));
+ return -ENOMEM;
+ refcount_set(&sysnames->usage, 1);
+ kill = sysnames;
- p = kbuf;
+ p = buf;
while ((s = strsep(&p, " \t\n"))) {
len = strlen(s);
if (len == 0)
sysnames->nr++;
}
- ret = size; /* consume everything, always */
+ if (sysnames->nr == 0) {
+ sysnames->subs[0] = sysnames->blank;
+ sysnames->nr++;
+ }
+
+ write_lock(&net->sysnames_lock);
+ kill = net->sysnames;
+ net->sysnames = sysnames;
+ write_unlock(&net->sysnames_lock);
+ ret = 0;
out:
- inode_unlock(file_inode(file));
- kfree(kbuf);
+ afs_put_sysnames(kill);
return ret;
invalid:
ret = -EINVAL;
error:
- sysnames->error = ret;
goto out;
}
-static int afs_proc_sysname_release(struct inode *inode, struct file *file)
+void afs_put_sysnames(struct afs_sysnames *sysnames)
{
- struct afs_sysnames *sysnames, *kill = NULL;
- struct seq_file *m = file->private_data;
- struct afs_net *net = afs_seq2net(m);
+ int i;
- sysnames = m->private;
- if (sysnames) {
- if (!sysnames->error) {
- kill = sysnames;
- if (sysnames->nr == 0) {
- sysnames->subs[0] = sysnames->blank;
- sysnames->nr++;
- }
- write_lock(&net->sysnames_lock);
- kill = net->sysnames;
- net->sysnames = sysnames;
- write_unlock(&net->sysnames_lock);
- }
- afs_put_sysnames(kill);
+ if (sysnames && refcount_dec_and_test(&sysnames->usage)) {
+ for (i = 0; i < sysnames->nr; i++)
+ if (sysnames->subs[i] != afs_init_sysname &&
+ sysnames->subs[i] != sysnames->blank)
+ kfree(sysnames->subs[i]);
}
-
- return seq_release(inode, file);
-}
-
-static void *afs_proc_sysname_start(struct seq_file *m, loff_t *pos)
- __acquires(&net->sysnames_lock)
-{
- struct afs_net *net = afs_seq2net(m);
- struct afs_sysnames *names = net->sysnames;
-
- read_lock(&net->sysnames_lock);
-
- if (*pos >= names->nr)
- return NULL;
- return (void *)(unsigned long)(*pos + 1);
-}
-
-static void *afs_proc_sysname_next(struct seq_file *m, void *v, loff_t *pos)
-{
- struct afs_net *net = afs_seq2net(m);
- struct afs_sysnames *names = net->sysnames;
-
- *pos += 1;
- if (*pos >= names->nr)
- return NULL;
- return (void *)(unsigned long)(*pos + 1);
-}
-
-static void afs_proc_sysname_stop(struct seq_file *m, void *v)
- __releases(&net->sysnames_lock)
-{
- struct afs_net *net = afs_seq2net(m);
-
- read_unlock(&net->sysnames_lock);
-}
-
-static int afs_proc_sysname_show(struct seq_file *m, void *v)
-{
- struct afs_net *net = afs_seq2net(m);
- struct afs_sysnames *sysnames = net->sysnames;
- unsigned int i = (unsigned long)v - 1;
-
- if (i < sysnames->nr)
- seq_printf(m, "%s\n", sysnames->subs[i]);
- return 0;
}
/*
*/
static int afs_proc_stats_show(struct seq_file *m, void *v)
{
- struct afs_net *net = afs_seq2net(m);
+ struct afs_net *net = afs_seq2net_single(m);
seq_puts(m, "kAFS statistics\n");
atomic_long_read(&net->n_store_bytes));
return 0;
}
+
+/*
+ * initialise /proc/fs/afs/<cell>/
+ */
+int afs_proc_cell_setup(struct afs_cell *cell)
+{
+ struct proc_dir_entry *dir;
+ struct afs_net *net = cell->net;
+
+ _enter("%p{%s},%p", cell, cell->name, net->proc_afs);
+
+ dir = proc_net_mkdir(net->net, cell->name, net->proc_afs);
+ if (!dir)
+ goto error_dir;
+
+ if (!proc_create_net_data("vlservers", 0444, dir,
+ &afs_proc_cell_vlservers_ops,
+ sizeof(struct seq_net_private),
+ cell) ||
+ !proc_create_net_data("volumes", 0444, dir,
+ &afs_proc_cell_volumes_ops,
+ sizeof(struct seq_net_private),
+ cell))
+ goto error_tree;
+
+ _leave(" = 0");
+ return 0;
+
+error_tree:
+ remove_proc_subtree(cell->name, net->proc_afs);
+error_dir:
+ _leave(" = -ENOMEM");
+ return -ENOMEM;
+}
+
+/*
+ * remove /proc/fs/afs/<cell>/
+ */
+void afs_proc_cell_remove(struct afs_cell *cell)
+{
+ struct afs_net *net = cell->net;
+
+ _enter("");
+ remove_proc_subtree(cell->name, net->proc_afs);
+ _leave("");
+}
+
+/*
+ * initialise the /proc/fs/afs/ directory
+ */
+int afs_proc_init(struct afs_net *net)
+{
+ struct proc_dir_entry *p;
+
+ _enter("");
+
+ p = proc_net_mkdir(net->net, "afs", net->net->proc_net);
+ if (!p)
+ goto error_dir;
+
+ if (!proc_create_net_data_write("cells", 0644, p,
+ &afs_proc_cells_ops,
+ afs_proc_cells_write,
+ sizeof(struct seq_net_private),
+ NULL) ||
+ !proc_create_net_single_write("rootcell", 0644, p,
+ afs_proc_rootcell_show,
+ afs_proc_rootcell_write,
+ NULL) ||
+ !proc_create_net("servers", 0444, p, &afs_proc_servers_ops,
+ sizeof(struct seq_net_private)) ||
+ !proc_create_net_single("stats", 0444, p, afs_proc_stats_show, NULL) ||
+ !proc_create_net_data_write("sysname", 0644, p,
+ &afs_proc_sysname_ops,
+ afs_proc_sysname_write,
+ sizeof(struct seq_net_private),
+ NULL))
+ goto error_tree;
+
+ net->proc_afs = p;
+ _leave(" = 0");
+ return 0;
+
+error_tree:
+ proc_remove(p);
+error_dir:
+ _leave(" = -ENOMEM");
+ return -ENOMEM;
+}
+
+/*
+ * clean up the /proc/fs/afs/ directory
+ */
+void afs_proc_cleanup(struct afs_net *net)
+{
+ proc_remove(net->proc_afs);
+ net->proc_afs = NULL;
+}
_enter("");
- ret = sock_create_kern(&init_net, AF_RXRPC, SOCK_DGRAM, PF_INET6, &socket);
+ ret = sock_create_kern(net->net, AF_RXRPC, SOCK_DGRAM, PF_INET6, &socket);
if (ret < 0)
goto error_1;
server->flags = (1UL << AFS_SERVER_FL_NEW);
server->update_at = ktime_get_real_seconds() + afs_server_update_delay;
rwlock_init(&server->fs_lock);
- INIT_LIST_HEAD(&server->cb_interests);
+ INIT_HLIST_HEAD(&server->cb_volumes);
rwlock_init(&server->cb_break_lock);
afs_inc_servers_outstanding(net);
};
MODULE_ALIAS_FS("afs");
+int afs_net_id;
+
static const struct super_operations afs_super_ops = {
.statfs = afs_statfs,
.alloc_inode = afs_alloc_inode,
/*
* clean up the filesystem
*/
-void __exit afs_fs_exit(void)
+void afs_fs_exit(void)
{
_enter("");
struct afs_super_info *as1 = data;
struct afs_super_info *as = AFS_FS_S(sb);
- return (as->net == as1->net &&
+ return (as->net_ns == as1->net_ns &&
as->volume &&
- as->volume->vid == as1->volume->vid);
+ as->volume->vid == as1->volume->vid &&
+ !as->dyn_root);
}
static int afs_dynroot_test_super(struct super_block *sb, void *data)
{
- return false;
+ struct afs_super_info *as1 = data;
+ struct afs_super_info *as = AFS_FS_S(sb);
+
+ return (as->net_ns == as1->net_ns &&
+ as->dyn_root);
}
static int afs_set_super(struct super_block *sb, void *data)
if (!sb->s_root)
goto error;
- if (params->dyn_root)
+ if (as->dyn_root) {
sb->s_d_op = &afs_dynroot_dentry_operations;
- else
+ ret = afs_dynroot_populate(sb);
+ if (ret < 0)
+ goto error;
+ } else {
sb->s_d_op = &afs_fs_dentry_operations;
+ }
_leave(" = 0");
return 0;
as = kzalloc(sizeof(struct afs_super_info), GFP_KERNEL);
if (as) {
- as->net = afs_get_net(params->net);
+ as->net_ns = get_net(params->net_ns);
if (params->dyn_root)
as->dyn_root = true;
else
{
if (as) {
afs_put_volume(as->cell, as->volume);
- afs_put_cell(as->net, as->cell);
- afs_put_net(as->net);
+ afs_put_cell(afs_net(as->net_ns), as->cell);
+ put_net(as->net_ns);
kfree(as);
}
}
+static void afs_kill_super(struct super_block *sb)
+{
+ struct afs_super_info *as = AFS_FS_S(sb);
+ struct afs_net *net = afs_net(as->net_ns);
+
+ if (as->dyn_root)
+ afs_dynroot_depopulate(sb);
+
+ /* Clear the callback interests (which will do ilookup5) before
+ * deactivating the superblock.
+ */
+ if (as->volume)
+ afs_clear_callback_interests(net, as->volume->servers);
+ kill_anon_super(sb);
+ if (as->volume)
+ afs_deactivate_volume(as->volume);
+ afs_destroy_sbi(as);
+}
+
/*
* get an AFS superblock
*/
_enter(",,%s,%p", dev_name, options);
memset(¶ms, 0, sizeof(params));
- params.net = &__afs_net;
ret = -EINVAL;
if (current->nsproxy->net_ns != &init_net)
goto error;
-
+ params.net_ns = current->nsproxy->net_ns;
+ params.net = afs_net(params.net_ns);
+
/* parse the options and device name */
if (options) {
ret = afs_parse_options(¶ms, options, &dev_name);
return ERR_PTR(ret);
}
-static void afs_kill_super(struct super_block *sb)
-{
- struct afs_super_info *as = AFS_FS_S(sb);
-
- /* Clear the callback interests (which will do ilookup5) before
- * deactivating the superblock.
- */
- if (as->volume)
- afs_clear_callback_interests(as->net, as->volume->servers);
- kill_anon_super(sb);
- if (as->volume)
- afs_deactivate_volume(as->volume);
- afs_destroy_sbi(as);
-}
-
/*
* Initialise an inode cache slab element prior to any use. Note that
* afs_alloc_inode() *must* reset anything that could incorrectly leak from one
if (mask && !(mask & req->events))
return 0;
- mask = file->f_op->poll_mask(file, req->events);
+ mask = file->f_op->poll_mask(file, req->events) & req->events;
if (!mask)
return 0;
spin_lock_irq(&ctx->ctx_lock);
spin_lock(&req->head->lock);
- mask = req->file->f_op->poll_mask(req->file, req->events);
+ mask = req->file->f_op->poll_mask(req->file, req->events) & req->events;
if (!mask) {
__add_wait_queue(req->head, &req->wait);
list_add_tail(&aiocb->ki_list, &ctx->active_reqs);
if (ia_valid & ATTR_GID)
inode->i_gid = attr->ia_gid;
if (ia_valid & ATTR_ATIME)
- inode->i_atime = timespec_trunc(attr->ia_atime,
- inode->i_sb->s_time_gran);
+ inode->i_atime = timespec64_trunc(attr->ia_atime,
+ inode->i_sb->s_time_gran);
if (ia_valid & ATTR_MTIME)
- inode->i_mtime = timespec_trunc(attr->ia_mtime,
- inode->i_sb->s_time_gran);
+ inode->i_mtime = timespec64_trunc(attr->ia_mtime,
+ inode->i_sb->s_time_gran);
if (ia_valid & ATTR_CTIME)
- inode->i_ctime = timespec_trunc(attr->ia_ctime,
- inode->i_sb->s_time_gran);
+ inode->i_ctime = timespec64_trunc(attr->ia_ctime,
+ inode->i_sb->s_time_gran);
if (ia_valid & ATTR_MODE) {
umode_t mode = attr->ia_mode;
struct inode *inode = dentry->d_inode;
umode_t mode = inode->i_mode;
int error;
- struct timespec now;
+ struct timespec64 now;
unsigned int ia_valid = attr->ia_valid;
WARN_ON_ONCE(!inode_is_locked(inode));
return -EIO;
}
-static int bad_inode_update_time(struct inode *inode, struct timespec *time,
+static int bad_inode_update_time(struct inode *inode, struct timespec64 *time,
int flags)
{
return -EIO;
(fs/nls/Config.in)
* Added Configure.help entries for CONFIG_BEFS_FS and CONFIG_DEBUG_BEFS
- (Documentation/Configure.help)
+ (currently at fs/befs/Kconfig)
2001-08-??
==========
if (size >= MAX_FILE_NOTE_SIZE) /* paranoia check */
return -EINVAL;
size = round_up(size, PAGE_SIZE);
- data = vmalloc(size);
- if (!data)
+ data = kvmalloc(size, GFP_KERNEL);
+ if (ZERO_OR_NULL_PTR(data))
return -ENOMEM;
start_end_ofs = data + 2;
filename = file_path(file, name_curpos, remaining);
if (IS_ERR(filename)) {
if (PTR_ERR(filename) == -ENAMETOOLONG) {
- vfree(data);
+ kvfree(data);
size = size * 5 / 4;
goto alloc;
}
kfree(t);
}
kfree(info->psinfo.data);
- vfree(info->files.data);
+ kvfree(info->files.data);
}
#else
/* Free data possibly allocated by fill_files_note(): */
if (info->notes_files)
- vfree(info->notes_files->data);
+ kvfree(info->notes_files->data);
kfree(info->prstatus);
kfree(info->psinfo);
if (segs - 1 > ULONG_MAX / sizeof(*vma_filesz))
goto end_coredump;
- vma_filesz = vmalloc(array_size(sizeof(*vma_filesz), (segs - 1)));
- if (!vma_filesz)
+ vma_filesz = kvmalloc(array_size(sizeof(*vma_filesz), (segs - 1)),
+ GFP_KERNEL);
+ if (ZERO_OR_NULL_PTR(vma_filesz))
goto end_coredump;
for (i = 0, vma = first_vma(current, gate_vma); vma != NULL;
cleanup:
free_note_info(&info);
kfree(shdr4extnum);
- vfree(vma_filesz);
+ kvfree(vma_filesz);
kfree(phdr4note);
kfree(elf);
out:
* Copyright (C) 1997 Richard Günther
*
* binfmt_misc detects binaries via a magic or filename extension and invokes
- * a specified wrapper. See Documentation/binfmt_misc.txt for more details.
+ * a specified wrapper. See Documentation/admin-guide/binfmt-misc.rst for more details.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
size_t size, struct bio *bio,
unsigned long bio_flags);
void btrfs_set_range_writeback(void *private_data, u64 start, u64 end);
-int btrfs_page_mkwrite(struct vm_fault *vmf);
+vm_fault_t btrfs_page_mkwrite(struct vm_fault *vmf);
int btrfs_readpage(struct file *file, struct page *page);
void btrfs_evict_inode(struct inode *inode);
int btrfs_write_inode(struct inode *inode, struct writeback_control *wbc);
offset_in_extent = em_start - em->start;
em_end = extent_map_end(em);
em_len = em_end - em_start;
- disko = 0;
+ disko = em->block_start + offset_in_extent;
flags = 0;
/*
u64 bytenr = em->block_start -
(em->start - em->orig_start);
- disko = em->block_start + offset_in_extent;
-
/*
* As btrfs supports shared space, this information
* can be exported to userspace tools via
static void update_time_for_write(struct inode *inode)
{
- struct timespec now;
+ struct timespec64 now;
if (IS_NOCMTIME(inode))
return;
now = current_time(inode);
- if (!timespec_equal(&inode->i_mtime, &now))
+ if (!timespec64_equal(&inode->i_mtime, &now))
inode->i_mtime = now;
- if (!timespec_equal(&inode->i_ctime, &now))
+ if (!timespec64_equal(&inode->i_ctime, &now))
inode->i_ctime = now;
if (IS_I_VERSION(inode))
inode->i_mtime = current_time(inode);
inode->i_atime = inode->i_mtime;
inode->i_ctime = inode->i_mtime;
- BTRFS_I(inode)->i_otime = inode->i_mtime;
+ BTRFS_I(inode)->i_otime = timespec64_to_timespec(inode->i_mtime);
return inode;
}
* This is a copy of file_update_time. We need this so we can return error on
* ENOSPC for updating the inode in the case of file write and mmap writes.
*/
-static int btrfs_update_time(struct inode *inode, struct timespec *now,
+static int btrfs_update_time(struct inode *inode, struct timespec64 *now,
int flags)
{
struct btrfs_root *root = BTRFS_I(inode)->root;
inode->i_mtime = current_time(inode);
inode->i_atime = inode->i_mtime;
inode->i_ctime = inode->i_mtime;
- BTRFS_I(inode)->i_otime = inode->i_mtime;
+ BTRFS_I(inode)->i_otime = timespec64_to_timespec(inode->i_mtime);
inode_item = btrfs_item_ptr(path->nodes[0], path->slots[0],
struct btrfs_inode_item);
* beyond EOF, then the page is guaranteed safe against truncation until we
* unlock the page.
*/
-int btrfs_page_mkwrite(struct vm_fault *vmf)
+vm_fault_t btrfs_page_mkwrite(struct vm_fault *vmf)
{
struct page *page = vmf->page;
struct inode *inode = file_inode(vmf->vma->vm_file);
char *kaddr;
unsigned long zero_start;
loff_t size;
- int ret;
+ vm_fault_t ret;
+ int ret2;
int reserved = 0;
u64 reserved_space;
u64 page_start;
* end up waiting indefinitely to get a lock on the page currently
* being processed by btrfs_page_mkwrite() function.
*/
- ret = btrfs_delalloc_reserve_space(inode, &data_reserved, page_start,
+ ret2 = btrfs_delalloc_reserve_space(inode, &data_reserved, page_start,
reserved_space);
- if (!ret) {
- ret = file_update_time(vmf->vma->vm_file);
+ if (!ret2) {
+ ret2 = file_update_time(vmf->vma->vm_file);
reserved = 1;
}
- if (ret) {
- if (ret == -ENOMEM)
- ret = VM_FAULT_OOM;
- else /* -ENOSPC, -EIO, etc */
- ret = VM_FAULT_SIGBUS;
+ if (ret2) {
+ ret = vmf_error(ret2);
if (reserved)
goto out;
goto out_noreserve;
EXTENT_DO_ACCOUNTING | EXTENT_DEFRAG,
0, 0, &cached_state);
- ret = btrfs_set_extent_delalloc(inode, page_start, end, 0,
+ ret2 = btrfs_set_extent_delalloc(inode, page_start, end, 0,
&cached_state, 0);
- if (ret) {
+ if (ret2) {
unlock_extent_cached(io_tree, page_start, page_end,
&cached_state);
ret = VM_FAULT_SIGBUS;
goto out_unlock;
}
- ret = 0;
+ ret2 = 0;
/* page is wholly or partially inside EOF */
if (page_start + PAGE_SIZE > size)
unlock_extent_cached(io_tree, page_start, page_end, &cached_state);
out_unlock:
- if (!ret) {
+ if (!ret2) {
btrfs_delalloc_release_extents(BTRFS_I(inode), PAGE_SIZE, true);
sb_end_pagefault(inode->i_sb);
extent_changeset_free(data_reserved);
struct btrfs_root *dest = BTRFS_I(new_dir)->root;
struct inode *new_inode = new_dentry->d_inode;
struct inode *old_inode = old_dentry->d_inode;
- struct timespec ctime = current_time(old_inode);
+ struct timespec64 ctime = current_time(old_inode);
struct dentry *parent;
u64 old_ino = btrfs_ino(BTRFS_I(old_inode));
u64 new_ino = btrfs_ino(BTRFS_I(new_inode));
struct btrfs_root *root = BTRFS_I(dir)->root;
struct btrfs_root *new_root;
struct btrfs_block_rsv block_rsv;
- struct timespec cur_time = current_time(dir);
+ struct timespec64 cur_time = current_time(dir);
struct inode *inode;
int ret;
int err;
}
temp_inode = btrfs_iget(sb, &key2, root, NULL);
+ if (IS_ERR(temp_inode)) {
+ ret = PTR_ERR(temp_inode);
+ goto out;
+ }
ret = inode_permission(temp_inode, MAY_READ | MAY_EXEC);
iput(temp_inode);
if (ret) {
struct btrfs_root *root = BTRFS_I(inode)->root;
struct btrfs_root_item *root_item = &root->root_item;
struct btrfs_trans_handle *trans;
- struct timespec ct = current_time(inode);
+ struct timespec64 ct = current_time(inode);
int ret = 0;
int received_uuid_changed;
struct btrfs_root *root)
{
struct btrfs_root_item *item = &root->root_item;
- struct timespec ct;
+ struct timespec64 ct;
- ktime_get_real_ts(&ct);
+ ktime_get_real_ts64(&ct);
spin_lock(&root->root_item_lock);
btrfs_set_root_ctransid(item, trans->transid);
btrfs_set_stack_timespec_sec(&item->ctime, ct.tv_sec);
have_csum = scrub_find_csum(sctx, logical, csum);
if (have_csum == 0)
++sctx->stat.no_csum;
- if (sctx->is_dev_replace && !have_csum) {
+ if (0 && sctx->is_dev_replace && !have_csum) {
ret = copy_nocow_pages(sctx, logical, l,
mirror_num,
physical_for_dev_replace);
struct dentry *dentry;
struct extent_buffer *tmp;
struct extent_buffer *old;
- struct timespec cur_time;
+ struct timespec64 cur_time;
int ret = 0;
u64 to_reserve = 0;
u64 index = 0;
*/
static int writepage_nounlock(struct page *page, struct writeback_control *wbc)
{
+ struct timespec ts;
struct inode *inode;
struct ceph_inode_info *ci;
struct ceph_fs_client *fsc;
set_bdi_congested(inode_to_bdi(inode), BLK_RW_ASYNC);
set_page_writeback(page);
+ ts = timespec64_to_timespec(inode->i_mtime);
err = ceph_osdc_writepages(&fsc->client->osdc, ceph_vino(inode),
&ci->i_layout, snapc, page_off, len,
ceph_wbc.truncate_seq,
ceph_wbc.truncate_size,
- &inode->i_mtime, &page, 1);
+ &ts, &page, 1);
if (err < 0) {
struct writeback_control tmp_wbc;
if (!wbc)
pages = NULL;
}
- req->r_mtime = inode->i_mtime;
+ req->r_mtime = timespec64_to_timespec(inode->i_mtime);
rc = ceph_osdc_start_request(&fsc->client->osdc, req, true);
BUG_ON(rc);
req = NULL;
goto out;
}
- req->r_mtime = inode->i_mtime;
+ req->r_mtime = timespec64_to_timespec(inode->i_mtime);
err = ceph_osdc_start_request(&fsc->client->osdc, req, false);
if (!err)
err = ceph_osdc_wait_request(&fsc->client->osdc, req);
goto out_put;
}
- req->r_mtime = inode->i_mtime;
+ req->r_mtime = timespec64_to_timespec(inode->i_mtime);
err = ceph_osdc_start_request(&fsc->client->osdc, req, false);
if (!err)
err = ceph_osdc_wait_request(&fsc->client->osdc, req);
0, false, true);
err = ceph_osdc_start_request(&fsc->client->osdc, rd_req, false);
- wr_req->r_mtime = ci->vfs_inode.i_mtime;
- wr_req->r_abort_on_full = true;
+ wr_req->r_mtime = timespec64_to_timespec(ci->vfs_inode.i_mtime);
err2 = ceph_osdc_start_request(&fsc->client->osdc, wr_req, false);
if (!err)
memset(&aux, 0, sizeof(aux));
aux.version = ci->i_version;
- aux.mtime = inode->i_mtime;
+ aux.mtime = timespec64_to_timespec(inode->i_mtime);
if (memcmp(data, &aux, sizeof(aux)) != 0)
return FSCACHE_CHECKAUX_OBSOLETE;
if (!ci->fscache) {
memset(&aux, 0, sizeof(aux));
aux.version = ci->i_version;
- aux.mtime = inode->i_mtime;
+ aux.mtime = timespec64_to_timespec(inode->i_mtime);
ci->fscache = fscache_acquire_cookie(fsc->fscache,
&ceph_fscache_inode_object_def,
&ci->i_vino, sizeof(ci->i_vino),
*s++ = 'w';
if (c & CEPH_CAP_GBUFFER)
*s++ = 'b';
+ if (c & CEPH_CAP_GWREXTEND)
+ *s++ = 'a';
if (c & CEPH_CAP_GLAZYIO)
*s++ = 'l';
return s;
arg.xattr_buf = NULL;
}
- arg.mtime = inode->i_mtime;
- arg.atime = inode->i_atime;
- arg.ctime = inode->i_ctime;
+ arg.mtime = timespec64_to_timespec(inode->i_mtime);
+ arg.atime = timespec64_to_timespec(inode->i_atime);
+ arg.ctime = timespec64_to_timespec(inode->i_ctime);
arg.op = op;
arg.caps = cap->implemented;
dput(prev);
}
+struct cap_extra_info {
+ struct ceph_string *pool_ns;
+ /* inline data */
+ u64 inline_version;
+ void *inline_data;
+ u32 inline_len;
+ /* dirstat */
+ bool dirstat_valid;
+ u64 nfiles;
+ u64 nsubdirs;
+ /* currently issued */
+ int issued;
+};
+
/*
* Handle a cap GRANT message from the MDS. (Note that a GRANT may
* actually be a revocation if it specifies a smaller cap set.)
*
* caller holds s_mutex and i_ceph_lock, we drop both.
*/
-static void handle_cap_grant(struct ceph_mds_client *mdsc,
- struct inode *inode, struct ceph_mds_caps *grant,
- struct ceph_string **pns, u64 inline_version,
- void *inline_data, u32 inline_len,
- struct ceph_buffer *xattr_buf,
+static void handle_cap_grant(struct inode *inode,
struct ceph_mds_session *session,
- struct ceph_cap *cap, int issued)
+ struct ceph_cap *cap,
+ struct ceph_mds_caps *grant,
+ struct ceph_buffer *xattr_buf,
+ struct cap_extra_info *extra_info)
__releases(ci->i_ceph_lock)
- __releases(mdsc->snap_rwsem)
+ __releases(session->s_mdsc->snap_rwsem)
{
struct ceph_inode_info *ci = ceph_inode(inode);
- int mds = session->s_mds;
int seq = le32_to_cpu(grant->seq);
int newcaps = le32_to_cpu(grant->caps);
int used, wanted, dirty;
u64 size = le64_to_cpu(grant->size);
u64 max_size = le64_to_cpu(grant->max_size);
- struct timespec mtime, atime, ctime;
int check_caps = 0;
bool wake = false;
bool writeback = false;
bool fill_inline = false;
dout("handle_cap_grant inode %p cap %p mds%d seq %d %s\n",
- inode, cap, mds, seq, ceph_cap_string(newcaps));
+ inode, cap, session->s_mds, seq, ceph_cap_string(newcaps));
dout(" size %llu max_size %llu, i_size %llu\n", size, max_size,
inode->i_size);
__check_cap_issue(ci, cap, newcaps);
if ((newcaps & CEPH_CAP_AUTH_SHARED) &&
- (issued & CEPH_CAP_AUTH_EXCL) == 0) {
+ (extra_info->issued & CEPH_CAP_AUTH_EXCL) == 0) {
inode->i_mode = le32_to_cpu(grant->mode);
inode->i_uid = make_kuid(&init_user_ns, le32_to_cpu(grant->uid));
inode->i_gid = make_kgid(&init_user_ns, le32_to_cpu(grant->gid));
from_kgid(&init_user_ns, inode->i_gid));
}
- if ((newcaps & CEPH_CAP_AUTH_SHARED) &&
- (issued & CEPH_CAP_LINK_EXCL) == 0) {
+ if ((newcaps & CEPH_CAP_LINK_SHARED) &&
+ (extra_info->issued & CEPH_CAP_LINK_EXCL) == 0) {
set_nlink(inode, le32_to_cpu(grant->nlink));
if (inode->i_nlink == 0 &&
(newcaps & (CEPH_CAP_LINK_SHARED | CEPH_CAP_LINK_EXCL)))
deleted_inode = true;
}
- if ((issued & CEPH_CAP_XATTR_EXCL) == 0 && grant->xattr_len) {
+ if ((extra_info->issued & CEPH_CAP_XATTR_EXCL) == 0 &&
+ grant->xattr_len) {
int len = le32_to_cpu(grant->xattr_len);
u64 version = le64_to_cpu(grant->xattr_version);
}
if (newcaps & CEPH_CAP_ANY_RD) {
+ struct timespec mtime, atime, ctime;
/* ctime/mtime/atime? */
ceph_decode_timespec(&mtime, &grant->mtime);
ceph_decode_timespec(&atime, &grant->atime);
ceph_decode_timespec(&ctime, &grant->ctime);
- ceph_fill_file_time(inode, issued,
+ ceph_fill_file_time(inode, extra_info->issued,
le32_to_cpu(grant->time_warp_seq),
&ctime, &mtime, &atime);
}
+ if ((newcaps & CEPH_CAP_FILE_SHARED) && extra_info->dirstat_valid) {
+ ci->i_files = extra_info->nfiles;
+ ci->i_subdirs = extra_info->nsubdirs;
+ }
+
if (newcaps & (CEPH_CAP_ANY_FILE_RD | CEPH_CAP_ANY_FILE_WR)) {
/* file layout may have changed */
s64 old_pool = ci->i_layout.pool_id;
ceph_file_layout_from_legacy(&ci->i_layout, &grant->layout);
old_ns = rcu_dereference_protected(ci->i_layout.pool_ns,
lockdep_is_held(&ci->i_ceph_lock));
- rcu_assign_pointer(ci->i_layout.pool_ns, *pns);
+ rcu_assign_pointer(ci->i_layout.pool_ns, extra_info->pool_ns);
- if (ci->i_layout.pool_id != old_pool || *pns != old_ns)
+ if (ci->i_layout.pool_id != old_pool ||
+ extra_info->pool_ns != old_ns)
ci->i_ceph_flags &= ~CEPH_I_POOL_PERM;
- *pns = old_ns;
+ extra_info->pool_ns = old_ns;
/* size/truncate_seq? */
- queue_trunc = ceph_fill_file_size(inode, issued,
+ queue_trunc = ceph_fill_file_size(inode, extra_info->issued,
le32_to_cpu(grant->truncate_seq),
le64_to_cpu(grant->truncate_size),
size);
}
BUG_ON(cap->issued & ~cap->implemented);
- if (inline_version > 0 && inline_version >= ci->i_inline_version) {
- ci->i_inline_version = inline_version;
+ if (extra_info->inline_version > 0 &&
+ extra_info->inline_version >= ci->i_inline_version) {
+ ci->i_inline_version = extra_info->inline_version;
if (ci->i_inline_version != CEPH_INLINE_NONE &&
(newcaps & (CEPH_CAP_FILE_CACHE|CEPH_CAP_FILE_LAZYIO)))
fill_inline = true;
}
if (le32_to_cpu(grant->op) == CEPH_CAP_OP_IMPORT) {
- if (newcaps & ~issued)
+ if (newcaps & ~extra_info->issued)
wake = true;
- kick_flushing_inode_caps(mdsc, session, inode);
- up_read(&mdsc->snap_rwsem);
+ kick_flushing_inode_caps(session->s_mdsc, session, inode);
+ up_read(&session->s_mdsc->snap_rwsem);
} else {
spin_unlock(&ci->i_ceph_lock);
}
if (fill_inline)
- ceph_fill_inline_data(inode, NULL, inline_data, inline_len);
+ ceph_fill_inline_data(inode, NULL, extra_info->inline_data,
+ extra_info->inline_len);
if (queue_trunc)
ceph_queue_vmtruncate(inode);
struct ceph_msg *msg)
{
struct ceph_mds_client *mdsc = session->s_mdsc;
- struct super_block *sb = mdsc->fsc->sb;
struct inode *inode;
struct ceph_inode_info *ci;
struct ceph_cap *cap;
struct ceph_mds_caps *h;
struct ceph_mds_cap_peer *peer = NULL;
struct ceph_snap_realm *realm = NULL;
- struct ceph_string *pool_ns = NULL;
- int mds = session->s_mds;
- int op, issued;
+ int op;
+ int msg_version = le16_to_cpu(msg->hdr.version);
u32 seq, mseq;
struct ceph_vino vino;
- u64 tid;
- u64 inline_version = 0;
- void *inline_data = NULL;
- u32 inline_len = 0;
void *snaptrace;
size_t snaptrace_len;
void *p, *end;
+ struct cap_extra_info extra_info = {};
- dout("handle_caps from mds%d\n", mds);
+ dout("handle_caps from mds%d\n", session->s_mds);
/* decode */
end = msg->front.iov_base + msg->front.iov_len;
- tid = le64_to_cpu(msg->hdr.tid);
if (msg->front.iov_len < sizeof(*h))
goto bad;
h = msg->front.iov_base;
snaptrace_len = le32_to_cpu(h->snap_trace_len);
p = snaptrace + snaptrace_len;
- if (le16_to_cpu(msg->hdr.version) >= 2) {
+ if (msg_version >= 2) {
u32 flock_len;
ceph_decode_32_safe(&p, end, flock_len, bad);
if (p + flock_len > end)
p += flock_len;
}
- if (le16_to_cpu(msg->hdr.version) >= 3) {
+ if (msg_version >= 3) {
if (op == CEPH_CAP_OP_IMPORT) {
if (p + sizeof(*peer) > end)
goto bad;
}
}
- if (le16_to_cpu(msg->hdr.version) >= 4) {
- ceph_decode_64_safe(&p, end, inline_version, bad);
- ceph_decode_32_safe(&p, end, inline_len, bad);
- if (p + inline_len > end)
+ if (msg_version >= 4) {
+ ceph_decode_64_safe(&p, end, extra_info.inline_version, bad);
+ ceph_decode_32_safe(&p, end, extra_info.inline_len, bad);
+ if (p + extra_info.inline_len > end)
goto bad;
- inline_data = p;
- p += inline_len;
+ extra_info.inline_data = p;
+ p += extra_info.inline_len;
}
- if (le16_to_cpu(msg->hdr.version) >= 5) {
+ if (msg_version >= 5) {
struct ceph_osd_client *osdc = &mdsc->fsc->client->osdc;
u32 epoch_barrier;
ceph_osdc_update_epoch_barrier(osdc, epoch_barrier);
}
- if (le16_to_cpu(msg->hdr.version) >= 8) {
+ if (msg_version >= 8) {
u64 flush_tid;
u32 caller_uid, caller_gid;
u32 pool_ns_len;
ceph_decode_32_safe(&p, end, pool_ns_len, bad);
if (pool_ns_len > 0) {
ceph_decode_need(&p, end, pool_ns_len, bad);
- pool_ns = ceph_find_or_create_string(p, pool_ns_len);
+ extra_info.pool_ns =
+ ceph_find_or_create_string(p, pool_ns_len);
p += pool_ns_len;
}
}
+ if (msg_version >= 11) {
+ struct ceph_timespec *btime;
+ u64 change_attr;
+ u32 flags;
+
+ /* version >= 9 */
+ if (p + sizeof(*btime) > end)
+ goto bad;
+ btime = p;
+ p += sizeof(*btime);
+ ceph_decode_64_safe(&p, end, change_attr, bad);
+ /* version >= 10 */
+ ceph_decode_32_safe(&p, end, flags, bad);
+ /* version >= 11 */
+ extra_info.dirstat_valid = true;
+ ceph_decode_64_safe(&p, end, extra_info.nfiles, bad);
+ ceph_decode_64_safe(&p, end, extra_info.nsubdirs, bad);
+ }
+
/* lookup ino */
- inode = ceph_find_inode(sb, vino);
+ inode = ceph_find_inode(mdsc->fsc->sb, vino);
ci = ceph_inode(inode);
dout(" op %s ino %llx.%llx inode %p\n", ceph_cap_op_name(op), vino.ino,
vino.snap, inode);
/* these will work even if we don't have a cap yet */
switch (op) {
case CEPH_CAP_OP_FLUSHSNAP_ACK:
- handle_cap_flushsnap_ack(inode, tid, h, session);
+ handle_cap_flushsnap_ack(inode, le64_to_cpu(msg->hdr.tid),
+ h, session);
goto done;
case CEPH_CAP_OP_EXPORT:
down_read(&mdsc->snap_rwsem);
}
handle_cap_import(mdsc, inode, h, peer, session,
- &cap, &issued);
- handle_cap_grant(mdsc, inode, h, &pool_ns,
- inline_version, inline_data, inline_len,
- msg->middle, session, cap, issued);
+ &cap, &extra_info.issued);
+ handle_cap_grant(inode, session, cap,
+ h, msg->middle, &extra_info);
if (realm)
ceph_put_snap_realm(mdsc, realm);
goto done_unlocked;
/* the rest require a cap */
spin_lock(&ci->i_ceph_lock);
- cap = __get_cap_for_mds(ceph_inode(inode), mds);
+ cap = __get_cap_for_mds(ceph_inode(inode), session->s_mds);
if (!cap) {
dout(" no cap on %p ino %llx.%llx from mds%d\n",
- inode, ceph_ino(inode), ceph_snap(inode), mds);
+ inode, ceph_ino(inode), ceph_snap(inode),
+ session->s_mds);
spin_unlock(&ci->i_ceph_lock);
goto flush_cap_releases;
}
switch (op) {
case CEPH_CAP_OP_REVOKE:
case CEPH_CAP_OP_GRANT:
- __ceph_caps_issued(ci, &issued);
- issued |= __ceph_caps_dirty(ci);
- handle_cap_grant(mdsc, inode, h, &pool_ns,
- inline_version, inline_data, inline_len,
- msg->middle, session, cap, issued);
+ __ceph_caps_issued(ci, &extra_info.issued);
+ extra_info.issued |= __ceph_caps_dirty(ci);
+ handle_cap_grant(inode, session, cap,
+ h, msg->middle, &extra_info);
goto done_unlocked;
case CEPH_CAP_OP_FLUSH_ACK:
- handle_cap_flush_ack(inode, tid, h, session, cap);
+ handle_cap_flush_ack(inode, le64_to_cpu(msg->hdr.tid),
+ h, session, cap);
break;
case CEPH_CAP_OP_TRUNC:
mutex_unlock(&session->s_mutex);
done_unlocked:
iput(inode);
- ceph_put_string(pool_ns);
+ ceph_put_string(extra_info.pool_ns);
return;
bad:
.release = ceph_release,
.unlocked_ioctl = ceph_ioctl,
.fsync = ceph_fsync,
+ .lock = ceph_lock,
+ .flock = ceph_flock,
};
const struct file_operations ceph_snapdir_fops = {
req->r_callback = ceph_aio_complete_req;
req->r_inode = inode;
req->r_priv = aio_req;
- req->r_abort_on_full = true;
ret = ceph_osdc_start_request(req->r_osdc, req, false);
out:
int num_pages = 0;
int flags;
int ret;
- struct timespec mtime = current_time(inode);
+ struct timespec mtime = timespec64_to_timespec(current_time(inode));
size_t count = iov_iter_count(iter);
loff_t pos = iocb->ki_pos;
bool write = iov_iter_rw(iter) == WRITE;
int flags;
int ret;
bool check_caps = false;
- struct timespec mtime = current_time(inode);
+ struct timespec mtime = timespec64_to_timespec(current_time(inode));
size_t count = iov_iter_count(from);
if (ceph_snap(file_inode(file)) != CEPH_NOSNAP)
goto out;
}
- req->r_mtime = inode->i_mtime;
+ req->r_mtime = timespec64_to_timespec(inode->i_mtime);
ret = ceph_osdc_start_request(&fsc->client->osdc, req, false);
if (!ret) {
ret = ceph_osdc_wait_request(&fsc->client->osdc, req);
struct timespec *mtime, struct timespec *atime)
{
struct ceph_inode_info *ci = ceph_inode(inode);
+ struct timespec64 ctime64 = timespec_to_timespec64(*ctime);
+ struct timespec64 mtime64 = timespec_to_timespec64(*mtime);
+ struct timespec64 atime64 = timespec_to_timespec64(*atime);
int warn = 0;
if (issued & (CEPH_CAP_FILE_EXCL|
CEPH_CAP_AUTH_EXCL|
CEPH_CAP_XATTR_EXCL)) {
if (ci->i_version == 0 ||
- timespec_compare(ctime, &inode->i_ctime) > 0) {
- dout("ctime %ld.%09ld -> %ld.%09ld inc w/ cap\n",
- inode->i_ctime.tv_sec, inode->i_ctime.tv_nsec,
- ctime->tv_sec, ctime->tv_nsec);
- inode->i_ctime = *ctime;
+ timespec64_compare(&ctime64, &inode->i_ctime) > 0) {
+ dout("ctime %lld.%09ld -> %lld.%09ld inc w/ cap\n",
+ (long long)inode->i_ctime.tv_sec, inode->i_ctime.tv_nsec,
+ (long long)ctime->tv_sec, ctime->tv_nsec);
+ inode->i_ctime = ctime64;
}
if (ci->i_version == 0 ||
ceph_seq_cmp(time_warp_seq, ci->i_time_warp_seq) > 0) {
/* the MDS did a utimes() */
- dout("mtime %ld.%09ld -> %ld.%09ld "
+ dout("mtime %lld.%09ld -> %lld.%09ld "
"tw %d -> %d\n",
- inode->i_mtime.tv_sec, inode->i_mtime.tv_nsec,
- mtime->tv_sec, mtime->tv_nsec,
+ (long long)inode->i_mtime.tv_sec, inode->i_mtime.tv_nsec,
+ (long long)mtime->tv_sec, mtime->tv_nsec,
ci->i_time_warp_seq, (int)time_warp_seq);
- inode->i_mtime = *mtime;
- inode->i_atime = *atime;
+ inode->i_mtime = mtime64;
+ inode->i_atime = atime64;
ci->i_time_warp_seq = time_warp_seq;
} else if (time_warp_seq == ci->i_time_warp_seq) {
/* nobody did utimes(); take the max */
- if (timespec_compare(mtime, &inode->i_mtime) > 0) {
- dout("mtime %ld.%09ld -> %ld.%09ld inc\n",
- inode->i_mtime.tv_sec,
+ if (timespec64_compare(&mtime64, &inode->i_mtime) > 0) {
+ dout("mtime %lld.%09ld -> %lld.%09ld inc\n",
+ (long long)inode->i_mtime.tv_sec,
inode->i_mtime.tv_nsec,
- mtime->tv_sec, mtime->tv_nsec);
- inode->i_mtime = *mtime;
+ (long long)mtime->tv_sec, mtime->tv_nsec);
+ inode->i_mtime = mtime64;
}
- if (timespec_compare(atime, &inode->i_atime) > 0) {
- dout("atime %ld.%09ld -> %ld.%09ld inc\n",
- inode->i_atime.tv_sec,
+ if (timespec64_compare(&atime64, &inode->i_atime) > 0) {
+ dout("atime %lld.%09ld -> %lld.%09ld inc\n",
+ (long long)inode->i_atime.tv_sec,
inode->i_atime.tv_nsec,
- atime->tv_sec, atime->tv_nsec);
- inode->i_atime = *atime;
+ (long long)atime->tv_sec, atime->tv_nsec);
+ inode->i_atime = atime64;
}
} else if (issued & CEPH_CAP_FILE_EXCL) {
/* we did a utimes(); ignore mds values */
} else {
/* we have no write|excl caps; whatever the MDS says is true */
if (ceph_seq_cmp(time_warp_seq, ci->i_time_warp_seq) >= 0) {
- inode->i_ctime = *ctime;
- inode->i_mtime = *mtime;
- inode->i_atime = *atime;
+ inode->i_ctime = ctime64;
+ inode->i_mtime = mtime64;
+ inode->i_atime = atime64;
ci->i_time_warp_seq = time_warp_seq;
} else {
warn = 1;
struct ceph_mds_client *mdsc = ceph_inode_to_client(inode)->mdsc;
struct ceph_mds_reply_inode *info = iinfo->in;
struct ceph_inode_info *ci = ceph_inode(inode);
- int issued = 0, implemented, new_issued;
+ int issued, new_issued, info_caps;
struct timespec mtime, atime, ctime;
struct ceph_buffer *xattr_blob = NULL;
struct ceph_string *pool_ns = NULL;
inode, ceph_vinop(inode), le64_to_cpu(info->version),
ci->i_version);
+ info_caps = le32_to_cpu(info->cap.caps);
+
/* prealloc new cap struct */
- if (info->cap.caps && ceph_snap(inode) == CEPH_NOSNAP)
+ if (info_caps && ceph_snap(inode) == CEPH_NOSNAP)
new_cap = ceph_get_cap(mdsc, caps_reservation);
/*
le64_to_cpu(info->version) > (ci->i_version & ~1)))
new_version = true;
- issued = __ceph_caps_issued(ci, &implemented);
- issued |= implemented | __ceph_caps_dirty(ci);
- new_issued = ~issued & le32_to_cpu(info->cap.caps);
+ __ceph_caps_issued(ci, &issued);
+ issued |= __ceph_caps_dirty(ci);
+ new_issued = ~issued & info_caps;
/* update inode */
inode->i_rdev = le32_to_cpu(info->rdev);
&ctime, &mtime, &atime);
}
+ if (new_version || (info_caps & CEPH_CAP_FILE_SHARED)) {
+ ci->i_files = le64_to_cpu(info->files);
+ ci->i_subdirs = le64_to_cpu(info->subdirs);
+ }
+
if (new_version ||
(new_issued & (CEPH_CAP_ANY_FILE_RD | CEPH_CAP_ANY_FILE_WR))) {
s64 old_pool = ci->i_layout.pool_id;
}
}
+ /* layout and rstat are not tracked by capability, update them if
+ * the inode info is from auth mds */
+ if (new_version || (info->cap.flags & CEPH_CAP_FLAG_AUTH)) {
+ if (S_ISDIR(inode->i_mode)) {
+ ci->i_dir_layout = iinfo->dir_layout;
+ ci->i_rbytes = le64_to_cpu(info->rbytes);
+ ci->i_rfiles = le64_to_cpu(info->rfiles);
+ ci->i_rsubdirs = le64_to_cpu(info->rsubdirs);
+ ceph_decode_timespec(&ci->i_rctime, &info->rctime);
+ }
+ }
+
/* xattrs */
/* note that if i_xattrs.len <= 4, i_xattrs.data will still be NULL. */
if ((ci->i_xattrs.version == 0 || !(issued & CEPH_CAP_XATTR_EXCL)) &&
}
/* finally update i_version */
- ci->i_version = le64_to_cpu(info->version);
+ if (le64_to_cpu(info->version) > ci->i_version)
+ ci->i_version = le64_to_cpu(info->version);
inode->i_mapping->a_ops = &ceph_aops;
case S_IFDIR:
inode->i_op = &ceph_dir_iops;
inode->i_fop = &ceph_dir_fops;
-
- ci->i_dir_layout = iinfo->dir_layout;
-
- ci->i_files = le64_to_cpu(info->files);
- ci->i_subdirs = le64_to_cpu(info->subdirs);
- ci->i_rbytes = le64_to_cpu(info->rbytes);
- ci->i_rfiles = le64_to_cpu(info->rfiles);
- ci->i_rsubdirs = le64_to_cpu(info->rsubdirs);
- ceph_decode_timespec(&ci->i_rctime, &info->rctime);
break;
default:
pr_err("fill_inode %llx.%llx BAD mode 0%o\n",
}
/* were we issued a capability? */
- if (info->cap.caps) {
+ if (info_caps) {
if (ceph_snap(inode) == CEPH_NOSNAP) {
- unsigned caps = le32_to_cpu(info->cap.caps);
ceph_add_cap(inode, session,
le64_to_cpu(info->cap.cap_id),
- cap_fmode, caps,
+ cap_fmode, info_caps,
le32_to_cpu(info->cap.wanted),
le32_to_cpu(info->cap.seq),
le32_to_cpu(info->cap.mseq),
/* set dir completion flag? */
if (S_ISDIR(inode->i_mode) &&
ci->i_files == 0 && ci->i_subdirs == 0 &&
- (caps & CEPH_CAP_FILE_SHARED) &&
+ (info_caps & CEPH_CAP_FILE_SHARED) &&
(issued & CEPH_CAP_FILE_EXCL) == 0 &&
!__ceph_dir_is_complete(ci)) {
dout(" marking %p complete (empty)\n", inode);
wake = true;
} else {
dout(" %p got snap_caps %s\n", inode,
- ceph_cap_string(le32_to_cpu(info->cap.caps)));
- ci->i_snap_caps |= le32_to_cpu(info->cap.caps);
+ ceph_cap_string(info_caps));
+ ci->i_snap_caps |= info_caps;
if (cap_fmode >= 0)
__ceph_get_fmode(ci, cap_fmode);
}
int cache_caps = CEPH_CAP_FILE_CACHE | CEPH_CAP_FILE_LAZYIO;
ci->i_inline_version = iinfo->inline_version;
if (ci->i_inline_version != CEPH_INLINE_NONE &&
- (locked_page ||
- (le32_to_cpu(info->cap.caps) & cache_caps)))
+ (locked_page || (info_caps & cache_caps)))
fill_inline = true;
}
int err = 0;
int inode_dirty_flags = 0;
bool lock_snap_rwsem = false;
+ struct timespec ts;
prealloc_cf = ceph_alloc_cap_flush();
if (!prealloc_cf)
}
if (ia_valid & ATTR_ATIME) {
- dout("setattr %p atime %ld.%ld -> %ld.%ld\n", inode,
- inode->i_atime.tv_sec, inode->i_atime.tv_nsec,
- attr->ia_atime.tv_sec, attr->ia_atime.tv_nsec);
+ dout("setattr %p atime %lld.%ld -> %lld.%ld\n", inode,
+ (long long)inode->i_atime.tv_sec, inode->i_atime.tv_nsec,
+ (long long)attr->ia_atime.tv_sec, attr->ia_atime.tv_nsec);
if (issued & CEPH_CAP_FILE_EXCL) {
ci->i_time_warp_seq++;
inode->i_atime = attr->ia_atime;
dirtied |= CEPH_CAP_FILE_EXCL;
} else if ((issued & CEPH_CAP_FILE_WR) &&
- timespec_compare(&inode->i_atime,
+ timespec64_compare(&inode->i_atime,
&attr->ia_atime) < 0) {
inode->i_atime = attr->ia_atime;
dirtied |= CEPH_CAP_FILE_WR;
} else if ((issued & CEPH_CAP_FILE_SHARED) == 0 ||
- !timespec_equal(&inode->i_atime, &attr->ia_atime)) {
- ceph_encode_timespec(&req->r_args.setattr.atime,
- &attr->ia_atime);
+ !timespec64_equal(&inode->i_atime, &attr->ia_atime)) {
+ ts = timespec64_to_timespec(attr->ia_atime);
+ ceph_encode_timespec(&req->r_args.setattr.atime, &ts);
mask |= CEPH_SETATTR_ATIME;
release |= CEPH_CAP_FILE_SHARED |
CEPH_CAP_FILE_RD | CEPH_CAP_FILE_WR;
}
}
if (ia_valid & ATTR_MTIME) {
- dout("setattr %p mtime %ld.%ld -> %ld.%ld\n", inode,
- inode->i_mtime.tv_sec, inode->i_mtime.tv_nsec,
- attr->ia_mtime.tv_sec, attr->ia_mtime.tv_nsec);
+ dout("setattr %p mtime %lld.%ld -> %lld.%ld\n", inode,
+ (long long)inode->i_mtime.tv_sec, inode->i_mtime.tv_nsec,
+ (long long)attr->ia_mtime.tv_sec, attr->ia_mtime.tv_nsec);
if (issued & CEPH_CAP_FILE_EXCL) {
ci->i_time_warp_seq++;
inode->i_mtime = attr->ia_mtime;
dirtied |= CEPH_CAP_FILE_EXCL;
} else if ((issued & CEPH_CAP_FILE_WR) &&
- timespec_compare(&inode->i_mtime,
+ timespec64_compare(&inode->i_mtime,
&attr->ia_mtime) < 0) {
inode->i_mtime = attr->ia_mtime;
dirtied |= CEPH_CAP_FILE_WR;
} else if ((issued & CEPH_CAP_FILE_SHARED) == 0 ||
- !timespec_equal(&inode->i_mtime, &attr->ia_mtime)) {
- ceph_encode_timespec(&req->r_args.setattr.mtime,
- &attr->ia_mtime);
+ !timespec64_equal(&inode->i_mtime, &attr->ia_mtime)) {
+ ts = timespec64_to_timespec(attr->ia_mtime);
+ ceph_encode_timespec(&req->r_args.setattr.mtime, &ts);
mask |= CEPH_SETATTR_MTIME;
release |= CEPH_CAP_FILE_SHARED |
CEPH_CAP_FILE_RD | CEPH_CAP_FILE_WR;
if (ia_valid & ATTR_CTIME) {
bool only = (ia_valid & (ATTR_SIZE|ATTR_MTIME|ATTR_ATIME|
ATTR_MODE|ATTR_UID|ATTR_GID)) == 0;
- dout("setattr %p ctime %ld.%ld -> %ld.%ld (%s)\n", inode,
- inode->i_ctime.tv_sec, inode->i_ctime.tv_nsec,
- attr->ia_ctime.tv_sec, attr->ia_ctime.tv_nsec,
+ dout("setattr %p ctime %lld.%ld -> %lld.%ld (%s)\n", inode,
+ (long long)inode->i_ctime.tv_sec, inode->i_ctime.tv_nsec,
+ (long long)attr->ia_ctime.tv_sec, attr->ia_ctime.tv_nsec,
only ? "ctime only" : "ignored");
if (only) {
/*
req->r_inode_drop = release;
req->r_args.setattr.mask = cpu_to_le32(mask);
req->r_num_caps = 1;
- req->r_stamp = attr->ia_ctime;
+ req->r_stamp = timespec64_to_timespec(attr->ia_ctime);
err = ceph_mdsc_do_request(mdsc, NULL, req);
}
dout("setattr %p result=%d (%s locally, %d remote)\n", inode, err,
struct ceph_fs_client *fsc = ceph_sb_to_client(inode->i_sb);
struct ceph_mds_client *mdsc = fsc->mdsc;
struct ceph_mds_request *req;
+ int mode;
int err;
if (ceph_snap(inode) == CEPH_SNAPDIR) {
if (!force && ceph_caps_issued_mask(ceph_inode(inode), mask, 1))
return 0;
- req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_GETATTR, USE_ANY_MDS);
+ mode = (mask & CEPH_STAT_RSTAT) ? USE_AUTH_MDS : USE_ANY_MDS;
+ req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_GETATTR, mode);
if (IS_ERR(req))
return PTR_ERR(req);
req->r_inode = inode;
stat->size = ci->i_files + ci->i_subdirs;
stat->blocks = 0;
stat->blksize = 65536;
+ /*
+ * Some applications rely on the number of st_nlink
+ * value on directories to be either 0 (if unlinked)
+ * or 2 + number of subdirectories.
+ */
+ if (stat->nlink == 1)
+ /* '.' + '..' + subdirs */
+ stat->nlink = 1 + 1 + ci->i_subdirs;
}
}
return err;
rec.v2.flock_len = (__force __le32)
((ci->i_ceph_flags & CEPH_I_ERROR_FILELOCK) ? 0 : 1);
} else {
+ struct timespec ts;
rec.v1.cap_id = cpu_to_le64(cap->cap_id);
rec.v1.wanted = cpu_to_le32(__ceph_caps_wanted(ci));
rec.v1.issued = cpu_to_le32(cap->issued);
rec.v1.size = cpu_to_le64(inode->i_size);
- ceph_encode_timespec(&rec.v1.mtime, &inode->i_mtime);
- ceph_encode_timespec(&rec.v1.atime, &inode->i_atime);
+ ts = timespec64_to_timespec(inode->i_mtime);
+ ceph_encode_timespec(&rec.v1.mtime, &ts);
+ ts = timespec64_to_timespec(inode->i_atime);
+ ceph_encode_timespec(&rec.v1.atime, &ts);
rec.v1.snaprealm = cpu_to_le64(ci->i_snap_realm->ino);
rec.v1.pathbase = cpu_to_le64(pathbase);
}
BUG_ON(capsnap->writing);
capsnap->size = inode->i_size;
- capsnap->mtime = inode->i_mtime;
- capsnap->atime = inode->i_atime;
- capsnap->ctime = inode->i_ctime;
+ capsnap->mtime = timespec64_to_timespec(inode->i_mtime);
+ capsnap->atime = timespec64_to_timespec(inode->i_atime);
+ capsnap->ctime = timespec64_to_timespec(inode->i_ctime);
capsnap->time_warp_seq = ci->i_time_warp_seq;
capsnap->truncate_size = ci->i_truncate_size;
capsnap->truncate_seq = ci->i_truncate_seq;
static int ceph_statfs(struct dentry *dentry, struct kstatfs *buf)
{
struct ceph_fs_client *fsc = ceph_inode_to_client(d_inode(dentry));
- struct ceph_monmap *monmap = fsc->client->monc.monmap;
+ struct ceph_mon_client *monc = &fsc->client->monc;
struct ceph_statfs st;
u64 fsid;
int err;
}
dout("statfs\n");
- err = ceph_monc_do_statfs(&fsc->client->monc, data_pool, &st);
+ err = ceph_monc_do_statfs(monc, data_pool, &st);
if (err < 0)
return err;
buf->f_namelen = NAME_MAX;
/* Must convert the fsid, for consistent values across arches */
- fsid = le64_to_cpu(*(__le64 *)(&monmap->fsid)) ^
- le64_to_cpu(*((__le64 *)&monmap->fsid + 1));
+ mutex_lock(&monc->mutex);
+ fsid = le64_to_cpu(*(__le64 *)(&monc->monmap->fsid)) ^
+ le64_to_cpu(*((__le64 *)&monc->monmap->fsid + 1));
+ mutex_unlock(&monc->mutex);
+
buf->f_fsid.val[0] = fsid & 0xffffffff;
buf->f_fsid.val[1] = fsid >> 32;
break;
/* misc */
case Opt_wsize:
- if (intval < PAGE_SIZE || intval > CEPH_MAX_WRITE_SIZE)
+ if (intval < (int)PAGE_SIZE || intval > CEPH_MAX_WRITE_SIZE)
return -EINVAL;
fsopt->wsize = ALIGN(intval, PAGE_SIZE);
break;
case Opt_rsize:
- if (intval < PAGE_SIZE || intval > CEPH_MAX_READ_SIZE)
+ if (intval < (int)PAGE_SIZE || intval > CEPH_MAX_READ_SIZE)
return -EINVAL;
fsopt->rsize = ALIGN(intval, PAGE_SIZE);
break;
case Opt_rasize:
if (intval < 0)
return -EINVAL;
- fsopt->rasize = ALIGN(intval + PAGE_SIZE - 1, PAGE_SIZE);
+ fsopt->rasize = ALIGN(intval, PAGE_SIZE);
break;
case Opt_caps_wanted_delay_min:
if (intval < 1)
fsopt->max_readdir = intval;
break;
case Opt_readdir_max_bytes:
- if (intval < PAGE_SIZE && intval != 0)
+ if (intval < (int)PAGE_SIZE && intval != 0)
return -EINVAL;
fsopt->max_readdir_bytes = intval;
break;
seq_puts(m, ",noasyncreaddir");
if ((fsopt->flags & CEPH_MOUNT_OPT_DCACHE) == 0)
seq_puts(m, ",nodcache");
+ if (fsopt->flags & CEPH_MOUNT_OPT_INO32)
+ seq_puts(m, ",ino32");
if (fsopt->flags & CEPH_MOUNT_OPT_FSCACHE) {
seq_show_option(m, "fsc", fsopt->fscache_uniq);
}
if (fsopt->mds_namespace)
seq_show_option(m, "mds_namespace", fsopt->mds_namespace);
- if (fsopt->wsize)
+ if (fsopt->wsize != CEPH_MAX_WRITE_SIZE)
seq_printf(m, ",wsize=%d", fsopt->wsize);
if (fsopt->rsize != CEPH_MAX_READ_SIZE)
seq_printf(m, ",rsize=%d", fsopt->rsize);
err = PTR_ERR(fsc->client);
goto fail;
}
+
fsc->client->extra_mon_dispatch = extra_mon_dispatch;
+ fsc->client->osdc.abort_on_full = true;
if (!fsopt->mds_namespace) {
ceph_monc_want_map(&fsc->client->monc, CEPH_SUB_MDSMAP,
return ERR_PTR(err);
}
+static void flush_fs_workqueues(struct ceph_fs_client *fsc)
+{
+ flush_workqueue(fsc->wb_wq);
+ flush_workqueue(fsc->pg_inv_wq);
+ flush_workqueue(fsc->trunc_wq);
+}
+
static void destroy_fs_client(struct ceph_fs_client *fsc)
{
dout("destroy_fs_client %p\n", fsc);
if (!fsc)
return;
fsc->mount_state = CEPH_MOUNT_SHUTDOWN;
+ ceph_osdc_abort_requests(&fsc->client->osdc, -EIO);
ceph_mdsc_force_umount(fsc->mdsc);
return;
}
dout("kill_sb %p\n", s);
ceph_mdsc_pre_umount(fsc->mdsc);
+ flush_fs_workqueues(fsc);
+
generic_shutdown_super(s);
fsc->client->extra_mon_dispatch = NULL;
size_t name_size; /* strlen(name) + 1 (for '\0') */
size_t (*getxattr_cb)(struct ceph_inode_info *ci, char *val,
size_t size);
- bool readonly, hidden;
bool (*exists_cb)(struct ceph_inode_info *ci);
+ unsigned int flags;
};
+#define VXATTR_FLAG_READONLY (1<<0)
+#define VXATTR_FLAG_HIDDEN (1<<1)
+#define VXATTR_FLAG_RSTAT (1<<2)
+
/* layouts */
static bool ceph_vxattrcb_layout_exists(struct ceph_inode_info *ci)
#define CEPH_XATTR_NAME2(_type, _name, _name2) \
XATTR_CEPH_PREFIX #_type "." #_name "." #_name2
-#define XATTR_NAME_CEPH(_type, _name) \
+#define XATTR_NAME_CEPH(_type, _name, _flags) \
{ \
.name = CEPH_XATTR_NAME(_type, _name), \
.name_size = sizeof (CEPH_XATTR_NAME(_type, _name)), \
.getxattr_cb = ceph_vxattrcb_ ## _type ## _ ## _name, \
- .readonly = true, \
- .hidden = false, \
- .exists_cb = NULL, \
+ .exists_cb = NULL, \
+ .flags = (VXATTR_FLAG_READONLY | _flags), \
}
+#define XATTR_RSTAT_FIELD(_type, _name) \
+ XATTR_NAME_CEPH(_type, _name, VXATTR_FLAG_RSTAT)
#define XATTR_LAYOUT_FIELD(_type, _name, _field) \
{ \
.name = CEPH_XATTR_NAME2(_type, _name, _field), \
.name_size = sizeof (CEPH_XATTR_NAME2(_type, _name, _field)), \
.getxattr_cb = ceph_vxattrcb_ ## _name ## _ ## _field, \
- .readonly = false, \
- .hidden = true, \
.exists_cb = ceph_vxattrcb_layout_exists, \
+ .flags = VXATTR_FLAG_HIDDEN, \
}
#define XATTR_QUOTA_FIELD(_type, _name) \
{ \
.name = CEPH_XATTR_NAME(_type, _name), \
.name_size = sizeof(CEPH_XATTR_NAME(_type, _name)), \
.getxattr_cb = ceph_vxattrcb_ ## _type ## _ ## _name, \
- .readonly = false, \
- .hidden = true, \
.exists_cb = ceph_vxattrcb_quota_exists, \
+ .flags = VXATTR_FLAG_HIDDEN, \
}
static struct ceph_vxattr ceph_dir_vxattrs[] = {
.name = "ceph.dir.layout",
.name_size = sizeof("ceph.dir.layout"),
.getxattr_cb = ceph_vxattrcb_layout,
- .readonly = false,
- .hidden = true,
.exists_cb = ceph_vxattrcb_layout_exists,
+ .flags = VXATTR_FLAG_HIDDEN,
},
XATTR_LAYOUT_FIELD(dir, layout, stripe_unit),
XATTR_LAYOUT_FIELD(dir, layout, stripe_count),
XATTR_LAYOUT_FIELD(dir, layout, object_size),
XATTR_LAYOUT_FIELD(dir, layout, pool),
XATTR_LAYOUT_FIELD(dir, layout, pool_namespace),
- XATTR_NAME_CEPH(dir, entries),
- XATTR_NAME_CEPH(dir, files),
- XATTR_NAME_CEPH(dir, subdirs),
- XATTR_NAME_CEPH(dir, rentries),
- XATTR_NAME_CEPH(dir, rfiles),
- XATTR_NAME_CEPH(dir, rsubdirs),
- XATTR_NAME_CEPH(dir, rbytes),
- XATTR_NAME_CEPH(dir, rctime),
+ XATTR_NAME_CEPH(dir, entries, 0),
+ XATTR_NAME_CEPH(dir, files, 0),
+ XATTR_NAME_CEPH(dir, subdirs, 0),
+ XATTR_RSTAT_FIELD(dir, rentries),
+ XATTR_RSTAT_FIELD(dir, rfiles),
+ XATTR_RSTAT_FIELD(dir, rsubdirs),
+ XATTR_RSTAT_FIELD(dir, rbytes),
+ XATTR_RSTAT_FIELD(dir, rctime),
{
.name = "ceph.quota",
.name_size = sizeof("ceph.quota"),
.getxattr_cb = ceph_vxattrcb_quota,
- .readonly = false,
- .hidden = true,
.exists_cb = ceph_vxattrcb_quota_exists,
+ .flags = VXATTR_FLAG_HIDDEN,
},
XATTR_QUOTA_FIELD(quota, max_bytes),
XATTR_QUOTA_FIELD(quota, max_files),
.name = "ceph.file.layout",
.name_size = sizeof("ceph.file.layout"),
.getxattr_cb = ceph_vxattrcb_layout,
- .readonly = false,
- .hidden = true,
.exists_cb = ceph_vxattrcb_layout_exists,
+ .flags = VXATTR_FLAG_HIDDEN,
},
XATTR_LAYOUT_FIELD(file, layout, stripe_unit),
XATTR_LAYOUT_FIELD(file, layout, stripe_count),
struct ceph_vxattr *vxattr;
size_t size = 0;
- for (vxattr = vxattrs; vxattr->name; vxattr++)
- if (!vxattr->hidden)
+ for (vxattr = vxattrs; vxattr->name; vxattr++) {
+ if (!(vxattr->flags & VXATTR_FLAG_HIDDEN))
size += vxattr->name_size;
+ }
return size;
}
/* let's see if a virtual xattr was requested */
vxattr = ceph_match_vxattr(inode, name);
if (vxattr) {
- err = ceph_do_getattr(inode, 0, true);
+ int mask = 0;
+ if (vxattr->flags & VXATTR_FLAG_RSTAT)
+ mask |= CEPH_STAT_RSTAT;
+ err = ceph_do_getattr(inode, mask, true);
if (err)
return err;
err = -ENODATA;
err = namelen;
if (vxattrs) {
for (i = 0; vxattrs[i].name; i++) {
- if (!vxattrs[i].hidden &&
+ if (!(vxattrs[i].flags & VXATTR_FLAG_HIDDEN) &&
!(vxattrs[i].exists_cb &&
!vxattrs[i].exists_cb(ci))) {
len = sprintf(names, "%s", vxattrs[i].name);
vxattr = ceph_match_vxattr(inode, name);
if (vxattr) {
- if (vxattr->readonly)
+ if (vxattr->flags & VXATTR_FLAG_READONLY)
return -EOPNOTSUPP;
if (value && !strncmp(vxattr->name, "ceph.quota", 10))
check_realm = true;
memset(&auxdata, 0, sizeof(auxdata));
auxdata.eof = cifsi->server_eof;
- auxdata.last_write_time = cifsi->vfs_inode.i_mtime;
- auxdata.last_change_time = cifsi->vfs_inode.i_ctime;
+ auxdata.last_write_time = timespec64_to_timespec(cifsi->vfs_inode.i_mtime);
+ auxdata.last_change_time = timespec64_to_timespec(cifsi->vfs_inode.i_ctime);
if (memcmp(data, &auxdata, datalen) != 0)
return FSCACHE_CHECKAUX_OBSOLETE;
seq_putc(m, '\n');
}
+static void
+cifs_dump_iface(struct seq_file *m, struct cifs_server_iface *iface)
+{
+ struct sockaddr_in *ipv4 = (struct sockaddr_in *)&iface->sockaddr;
+ struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)&iface->sockaddr;
+
+ seq_printf(m, "\t\tSpeed: %zu bps\n", iface->speed);
+ seq_puts(m, "\t\tCapabilities: ");
+ if (iface->rdma_capable)
+ seq_puts(m, "rdma ");
+ if (iface->rss_capable)
+ seq_puts(m, "rss ");
+ seq_putc(m, '\n');
+ if (iface->sockaddr.ss_family == AF_INET)
+ seq_printf(m, "\t\tIPv4: %pI4\n", &ipv4->sin_addr);
+ else if (iface->sockaddr.ss_family == AF_INET6)
+ seq_printf(m, "\t\tIPv6: %pI6\n", &ipv6->sin6_addr);
+}
+
static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
{
struct list_head *tmp1, *tmp2, *tmp3;
mid_entry->mid);
}
spin_unlock(&GlobalMid_Lock);
+
+ spin_lock(&ses->iface_lock);
+ if (ses->iface_count)
+ seq_printf(m, "\n\tServer interfaces: %zu\n",
+ ses->iface_count);
+ for (j = 0; j < ses->iface_count; j++) {
+ seq_printf(m, "\t%d)\n", j);
+ cifs_dump_iface(m, &ses->iface_list[j]);
+ }
+ spin_unlock(&ses->iface_lock);
}
}
spin_unlock(&cifs_tcp_ses_lock);
#include <crypto/aead.h>
int __cifs_calc_signature(struct smb_rqst *rqst,
- int start,
struct TCP_Server_Info *server, char *signature,
struct shash_desc *shash)
{
int rc;
struct kvec *iov = rqst->rq_iov;
int n_vec = rqst->rq_nvec;
+ int is_smb2 = server->vals->header_preamble_size == 0;
- for (i = start; i < n_vec; i++) {
+ /* iov[0] is actual data and not the rfc1002 length for SMB2+ */
+ if (is_smb2) {
+ if (iov[0].iov_len <= 4)
+ return -EIO;
+ i = 0;
+ } else {
+ if (n_vec < 2 || iov[0].iov_len != 4)
+ return -EIO;
+ i = 1; /* skip rfc1002 length */
+ }
+
+ for (; i < n_vec; i++) {
if (iov[i].iov_len == 0)
continue;
if (iov[i].iov_base == NULL) {
cifs_dbg(VFS, "null iovec entry\n");
return -EIO;
}
- if (i == 1 && iov[1].iov_len <= 4)
- break; /* nothing to sign or corrupt header */
+
rc = crypto_shash_update(shash,
iov[i].iov_base, iov[i].iov_len);
if (rc) {
return rc;
}
- return __cifs_calc_signature(rqst, 1, server, signature,
+ return __cifs_calc_signature(rqst, server, signature,
&server->secmech.sdescmd5->shash);
}
#define CIFS_MAGIC_NUMBER 0xFF534D42 /* the first four bytes of SMB PDUs */
+#define CIFS_PORT 445
+#define RFC1001_PORT 139
+
/*
* The sizes of various internal tables and strings
*/
/* send echo request */
int (*echo)(struct TCP_Server_Info *);
/* create directory */
+ int (*posix_mkdir)(const unsigned int xid, struct inode *inode,
+ umode_t mode, struct cifs_tcon *tcon,
+ const char *full_path,
+ struct cifs_sb_info *cifs_sb);
int (*mkdir)(const unsigned int, struct cifs_tcon *, const char *,
struct cifs_sb_info *);
/* set info on created directory */
#endif
+struct cifs_server_iface {
+ size_t speed;
+ unsigned int rdma_capable : 1;
+ unsigned int rss_capable : 1;
+ struct sockaddr_storage sockaddr;
+};
+
/*
* Session structure. One of these for each uid session with a particular host
*/
#ifdef CONFIG_CIFS_SMB311
__u8 preauth_sha_hash[SMB2_PREAUTH_HASH_SIZE];
#endif /* 3.1.1 */
+
+ /*
+ * Network interfaces available on the server this session is
+ * connected to.
+ *
+ * Other channels can be opened by connecting and binding this
+ * session to interfaces from this list.
+ *
+ * iface_lock should be taken when accessing any of these fields
+ */
+ spinlock_t iface_lock;
+ struct cifs_server_iface *iface_list;
+ size_t iface_count;
+ unsigned long iface_last_update; /* jiffies */
};
static inline bool
return ses->server->vals->cap_unix & ses->capabilities;
}
+struct cached_fid {
+ bool is_valid:1; /* Do we have a useable root fid */
+ struct cifs_fid *fid;
+ struct mutex fid_mutex;
+ struct cifs_tcon *tcon;
+ struct work_struct lease_break;
+};
+
/*
* there is one of these for each connection to a resource on a particular
* session
struct fscache_cookie *fscache; /* cookie for share */
#endif
struct list_head pending_opens; /* list of incomplete opens */
- bool valid_root_fid:1; /* Do we have a useable root fid */
- struct mutex prfid_mutex; /* prevents reopen race after dead ses*/
- struct cifs_fid *prfid; /* handle to the directory at top of share */
+ struct cached_fid crfid; /* Cached root fid */
/* BB add field for back pointer to sb struct(s)? */
};
struct kvec *, int /* nvec to send */,
int * /* type of buf returned */, const int flags,
struct kvec * /* resp vec */);
-extern int smb2_send_recv(const unsigned int xid, struct cifs_ses *pses,
- struct kvec *pkvec, int nvec_to_send,
- int *pbuftype, const int flags,
- struct kvec *presp);
extern int SendReceiveBlockingLock(const unsigned int xid,
struct cifs_tcon *ptcon,
struct smb_hdr *in_buf ,
struct cifs_sb_info *cifs_sb,
const unsigned char *path, char *pbuf,
unsigned int *pbytes_written);
-int __cifs_calc_signature(struct smb_rqst *rqst, int start,
+int __cifs_calc_signature(struct smb_rqst *rqst,
struct TCP_Server_Info *server, char *signature,
struct shash_desc *shash);
enum securityEnum cifs_select_sectype(struct TCP_Server_Info *,
struct cifs_aio_ctx *cifs_aio_ctx_alloc(void);
void cifs_aio_ctx_release(struct kref *refcount);
int setup_aio_ctx_iter(struct cifs_aio_ctx *ctx, struct iov_iter *iter, int rw);
+void smb2_cached_lease_break(struct work_struct *work);
int cifs_alloc_hash(const char *name, struct crypto_shash **shash,
struct sdesc **sdesc);
}
spin_unlock(&tcon->open_file_lock);
- mutex_lock(&tcon->prfid_mutex);
- tcon->valid_root_fid = false;
- memset(tcon->prfid, 0, sizeof(struct cifs_fid));
- mutex_unlock(&tcon->prfid_mutex);
+ mutex_lock(&tcon->crfid.fid_mutex);
+ tcon->crfid.is_valid = false;
+ memset(tcon->crfid.fid, 0, sizeof(struct cifs_fid));
+ mutex_unlock(&tcon->crfid.fid_mutex);
/*
* BB Add call to invalidate_inodes(sb) for all superblocks mounted
#include "smb2proto.h"
#include "smbdirect.h"
-#define CIFS_PORT 445
-#define RFC1001_PORT 139
-
extern mempool_t *cifs_req_poolp;
extern bool disable_legacy_dialects;
#ifdef CONFIG_CIFS_SMB311
if ((volume_info->linux_ext) && (ses->server->posix_ext_supported)) {
- if (ses->server->vals->protocol_id == SMB311_PROT_ID)
+ if (ses->server->vals->protocol_id == SMB311_PROT_ID) {
tcon->posix_extensions = true;
+ printk_once(KERN_WARNING
+ "SMB3.11 POSIX Extensions are experimental\n");
+ }
}
#endif /* 311 */
memset(&auxdata, 0, sizeof(auxdata));
auxdata.eof = cifsi->server_eof;
- auxdata.last_write_time = cifsi->vfs_inode.i_mtime;
- auxdata.last_change_time = cifsi->vfs_inode.i_ctime;
+ auxdata.last_write_time = timespec64_to_timespec(cifsi->vfs_inode.i_mtime);
+ auxdata.last_change_time = timespec64_to_timespec(cifsi->vfs_inode.i_ctime);
cifsi->fscache =
fscache_acquire_cookie(tcon->fscache,
if (cifsi->fscache) {
memset(&auxdata, 0, sizeof(auxdata));
auxdata.eof = cifsi->server_eof;
- auxdata.last_write_time = cifsi->vfs_inode.i_mtime;
- auxdata.last_change_time = cifsi->vfs_inode.i_ctime;
+ auxdata.last_write_time = timespec64_to_timespec(cifsi->vfs_inode.i_mtime);
+ auxdata.last_change_time = timespec64_to_timespec(cifsi->vfs_inode.i_ctime);
cifs_dbg(FYI, "%s: (0x%p)\n", __func__, cifsi->fscache);
fscache_relinquish_cookie(cifsi->fscache, &auxdata, false);
cifs_revalidate_cache(struct inode *inode, struct cifs_fattr *fattr)
{
struct cifsInodeInfo *cifs_i = CIFS_I(inode);
+ struct timespec ts;
cifs_dbg(FYI, "%s: revalidating inode %llu\n",
__func__, cifs_i->uniqueid);
}
/* revalidate if mtime or size have changed */
- if (timespec_equal(&inode->i_mtime, &fattr->cf_mtime) &&
+ ts = timespec64_to_timespec(inode->i_mtime);
+ if (timespec_equal(&ts, &fattr->cf_mtime) &&
cifs_i->server_eof == fattr->cf_eof) {
cifs_dbg(FYI, "%s: inode %llu is unchanged\n",
__func__, cifs_i->uniqueid);
cifs_revalidate_cache(inode, fattr);
spin_lock(&inode->i_lock);
- inode->i_atime = fattr->cf_atime;
- inode->i_mtime = fattr->cf_mtime;
- inode->i_ctime = fattr->cf_ctime;
+ inode->i_atime = timespec_to_timespec64(fattr->cf_atime);
+ inode->i_mtime = timespec_to_timespec64(fattr->cf_mtime);
+ inode->i_ctime = timespec_to_timespec64(fattr->cf_ctime);
inode->i_rdev = fattr->cf_rdev;
cifs_nlink_fattr_to_inode(inode, fattr);
inode->i_uid = fattr->cf_uid;
if (attrs->ia_valid & ATTR_ATIME) {
set_time = true;
info_buf.LastAccessTime =
- cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_atime));
+ cpu_to_le64(cifs_UnixTimeToNT(timespec64_to_timespec(attrs->ia_atime)));
} else
info_buf.LastAccessTime = 0;
if (attrs->ia_valid & ATTR_MTIME) {
set_time = true;
info_buf.LastWriteTime =
- cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_mtime));
+ cpu_to_le64(cifs_UnixTimeToNT(timespec64_to_timespec(attrs->ia_mtime)));
} else
info_buf.LastWriteTime = 0;
if (set_time && (attrs->ia_valid & ATTR_CTIME)) {
cifs_dbg(FYI, "CIFS - CTIME changed\n");
info_buf.ChangeTime =
- cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_ctime));
+ cpu_to_le64(cifs_UnixTimeToNT(timespec64_to_timespec(attrs->ia_ctime)));
} else
info_buf.ChangeTime = 0;
goto mkdir_out;
}
+ server = tcon->ses->server;
+
+#ifdef CONFIG_CIFS_SMB311
+ if ((server->ops->posix_mkdir) && (tcon->posix_extensions)) {
+ rc = server->ops->posix_mkdir(xid, inode, mode, tcon, full_path,
+ cifs_sb);
+ d_drop(direntry); /* for time being always refresh inode info */
+ goto mkdir_out;
+ }
+#endif /* SMB311 */
+
if (cap_unix(tcon->ses) && (CIFS_UNIX_POSIX_PATH_OPS_CAP &
le64_to_cpu(tcon->fsUnixInfo.Capability))) {
rc = cifs_posix_mkdir(inode, direntry, mode, full_path, cifs_sb,
goto mkdir_out;
}
- server = tcon->ses->server;
-
if (!server->ops->mkdir) {
rc = -ENOSYS;
goto mkdir_out;
/* old CIFS Unix Extensions doesn't return create time */
if (CIFS_I(inode)->createtime) {
stat->result_mask |= STATX_BTIME;
- stat->btime =
- cifs_NTtimeToUnix(cpu_to_le64(CIFS_I(inode)->createtime));
+ stat->btime = timespec_to_timespec64(
+ cifs_NTtimeToUnix(cpu_to_le64(CIFS_I(inode)->createtime)));
}
stat->attributes_mask |= (STATX_ATTR_COMPRESSED | STATX_ATTR_ENCRYPTED);
args->gid = INVALID_GID; /* no change */
if (attrs->ia_valid & ATTR_ATIME)
- args->atime = cifs_UnixTimeToNT(attrs->ia_atime);
+ args->atime = cifs_UnixTimeToNT(timespec64_to_timespec(attrs->ia_atime));
else
args->atime = NO_CHANGE_64;
if (attrs->ia_valid & ATTR_MTIME)
- args->mtime = cifs_UnixTimeToNT(attrs->ia_mtime);
+ args->mtime = cifs_UnixTimeToNT(timespec64_to_timespec(attrs->ia_mtime));
else
args->mtime = NO_CHANGE_64;
if (attrs->ia_valid & ATTR_CTIME)
- args->ctime = cifs_UnixTimeToNT(attrs->ia_ctime);
+ args->ctime = cifs_UnixTimeToNT(timespec64_to_timespec(attrs->ia_ctime));
else
args->ctime = NO_CHANGE_64;
INIT_LIST_HEAD(&ret_buf->smb_ses_list);
INIT_LIST_HEAD(&ret_buf->tcon_list);
mutex_init(&ret_buf->session_mutex);
+ spin_lock_init(&ret_buf->iface_lock);
}
return ret_buf;
}
kfree(buf_to_free->user_name);
kfree(buf_to_free->domainName);
kzfree(buf_to_free->auth_key.response);
+ kfree(buf_to_free->iface_list);
kzfree(buf_to_free);
}
INIT_LIST_HEAD(&ret_buf->openFileList);
INIT_LIST_HEAD(&ret_buf->tcon_list);
spin_lock_init(&ret_buf->open_file_lock);
- mutex_init(&ret_buf->prfid_mutex);
- ret_buf->prfid = kzalloc(sizeof(struct cifs_fid), GFP_KERNEL);
+ mutex_init(&ret_buf->crfid.fid_mutex);
+ ret_buf->crfid.fid = kzalloc(sizeof(struct cifs_fid),
+ GFP_KERNEL);
#ifdef CONFIG_CIFS_STATS
spin_lock_init(&ret_buf->stat_lock);
#endif
atomic_dec(&tconInfoAllocCount);
kfree(buf_to_free->nativeFileSystem);
kzfree(buf_to_free->password);
- kfree(buf_to_free->prfid);
+ kfree(buf_to_free->crfid.fid);
kfree(buf_to_free);
}
#ifdef CONFIG_CIFS_SMB311
/* SMB311 POSIX extensions paths do not include leading slash */
else if (cifs_sb_master_tlink(cifs_sb) &&
- cifs_sb_master_tcon(cifs_sb)->posix_extensions) {
+ cifs_sb_master_tcon(cifs_sb)->posix_extensions &&
+ (from[0] == '/')) {
start_of_path = from + 1;
}
#endif /* 311 */
{
struct smb2_lease_break_work *lw = container_of(work,
struct smb2_lease_break_work, lease_break);
- int rc;
+ int rc = 0;
rc = SMB2_lease_break(0, tlink_tcon(lw->tlink), lw->lease_key,
lw->lease_state);
+
cifs_dbg(FYI, "Lease release rc %d\n", rc);
cifs_put_tlink(lw->tlink);
kfree(lw);
open->oplock = lease_state;
}
+
return found;
}
return true;
}
spin_unlock(&tcon->open_file_lock);
+
+ if (tcon->crfid.is_valid &&
+ !memcmp(rsp->LeaseKey,
+ tcon->crfid.fid->lease_key,
+ SMB2_LEASE_KEY_SIZE)) {
+ INIT_WORK(&tcon->crfid.lease_break,
+ smb2_cached_lease_break);
+ queue_work(cifsiod_wq,
+ &tcon->crfid.lease_break);
+ spin_unlock(&cifs_tcp_ses_lock);
+ return true;
+ }
}
}
}
return rsize;
}
-#ifdef CONFIG_CIFS_STATS2
+
+static int
+parse_server_interfaces(struct network_interface_info_ioctl_rsp *buf,
+ size_t buf_len,
+ struct cifs_server_iface **iface_list,
+ size_t *iface_count)
+{
+ struct network_interface_info_ioctl_rsp *p;
+ struct sockaddr_in *addr4;
+ struct sockaddr_in6 *addr6;
+ struct iface_info_ipv4 *p4;
+ struct iface_info_ipv6 *p6;
+ struct cifs_server_iface *info;
+ ssize_t bytes_left;
+ size_t next = 0;
+ int nb_iface = 0;
+ int rc = 0;
+
+ *iface_list = NULL;
+ *iface_count = 0;
+
+ /*
+ * Fist pass: count and sanity check
+ */
+
+ bytes_left = buf_len;
+ p = buf;
+ while (bytes_left >= sizeof(*p)) {
+ nb_iface++;
+ next = le32_to_cpu(p->Next);
+ if (!next) {
+ bytes_left -= sizeof(*p);
+ break;
+ }
+ p = (struct network_interface_info_ioctl_rsp *)((u8 *)p+next);
+ bytes_left -= next;
+ }
+
+ if (!nb_iface) {
+ cifs_dbg(VFS, "%s: malformed interface info\n", __func__);
+ rc = -EINVAL;
+ goto out;
+ }
+
+ if (bytes_left || p->Next)
+ cifs_dbg(VFS, "%s: incomplete interface info\n", __func__);
+
+
+ /*
+ * Second pass: extract info to internal structure
+ */
+
+ *iface_list = kcalloc(nb_iface, sizeof(**iface_list), GFP_KERNEL);
+ if (!*iface_list) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ info = *iface_list;
+ bytes_left = buf_len;
+ p = buf;
+ while (bytes_left >= sizeof(*p)) {
+ info->speed = le64_to_cpu(p->LinkSpeed);
+ info->rdma_capable = le32_to_cpu(p->Capability & RDMA_CAPABLE);
+ info->rss_capable = le32_to_cpu(p->Capability & RSS_CAPABLE);
+
+ cifs_dbg(FYI, "%s: adding iface %zu\n", __func__, *iface_count);
+ cifs_dbg(FYI, "%s: speed %zu bps\n", __func__, info->speed);
+ cifs_dbg(FYI, "%s: capabilities 0x%08x\n", __func__,
+ le32_to_cpu(p->Capability));
+
+ switch (p->Family) {
+ /*
+ * The kernel and wire socket structures have the same
+ * layout and use network byte order but make the
+ * conversion explicit in case either one changes.
+ */
+ case INTERNETWORK:
+ addr4 = (struct sockaddr_in *)&info->sockaddr;
+ p4 = (struct iface_info_ipv4 *)p->Buffer;
+ addr4->sin_family = AF_INET;
+ memcpy(&addr4->sin_addr, &p4->IPv4Address, 4);
+
+ /* [MS-SMB2] 2.2.32.5.1.1 Clients MUST ignore these */
+ addr4->sin_port = cpu_to_be16(CIFS_PORT);
+
+ cifs_dbg(FYI, "%s: ipv4 %pI4\n", __func__,
+ &addr4->sin_addr);
+ break;
+ case INTERNETWORKV6:
+ addr6 = (struct sockaddr_in6 *)&info->sockaddr;
+ p6 = (struct iface_info_ipv6 *)p->Buffer;
+ addr6->sin6_family = AF_INET6;
+ memcpy(&addr6->sin6_addr, &p6->IPv6Address, 16);
+
+ /* [MS-SMB2] 2.2.32.5.1.2 Clients MUST ignore these */
+ addr6->sin6_flowinfo = 0;
+ addr6->sin6_scope_id = 0;
+ addr6->sin6_port = cpu_to_be16(CIFS_PORT);
+
+ cifs_dbg(FYI, "%s: ipv6 %pI6\n", __func__,
+ &addr6->sin6_addr);
+ break;
+ default:
+ cifs_dbg(VFS,
+ "%s: skipping unsupported socket family\n",
+ __func__);
+ goto next_iface;
+ }
+
+ (*iface_count)++;
+ info++;
+next_iface:
+ next = le32_to_cpu(p->Next);
+ if (!next)
+ break;
+ p = (struct network_interface_info_ioctl_rsp *)((u8 *)p+next);
+ bytes_left -= next;
+ }
+
+ if (!*iface_count) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+out:
+ if (rc) {
+ kfree(*iface_list);
+ *iface_count = 0;
+ *iface_list = NULL;
+ }
+ return rc;
+}
+
+
static int
SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon)
{
int rc;
unsigned int ret_data_len = 0;
- struct network_interface_info_ioctl_rsp *out_buf;
+ struct network_interface_info_ioctl_rsp *out_buf = NULL;
+ struct cifs_server_iface *iface_list;
+ size_t iface_count;
+ struct cifs_ses *ses = tcon->ses;
rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID,
FSCTL_QUERY_NETWORK_INTERFACE_INFO, true /* is_fsctl */,
NULL /* no data input */, 0 /* no data input */,
(char **)&out_buf, &ret_data_len);
- if (rc != 0)
+ if (rc != 0) {
cifs_dbg(VFS, "error %d on ioctl to get interface list\n", rc);
- else if (ret_data_len < sizeof(struct network_interface_info_ioctl_rsp)) {
- cifs_dbg(VFS, "server returned bad net interface info buf\n");
- rc = -EINVAL;
- } else {
- /* Dump info on first interface */
- cifs_dbg(FYI, "Adapter Capability 0x%x\t",
- le32_to_cpu(out_buf->Capability));
- cifs_dbg(FYI, "Link Speed %lld\n",
- le64_to_cpu(out_buf->LinkSpeed));
+ goto out;
}
+
+ rc = parse_server_interfaces(out_buf, ret_data_len,
+ &iface_list, &iface_count);
+ if (rc)
+ goto out;
+
+ spin_lock(&ses->iface_lock);
+ kfree(ses->iface_list);
+ ses->iface_list = iface_list;
+ ses->iface_count = iface_count;
+ ses->iface_last_update = jiffies;
+ spin_unlock(&ses->iface_lock);
+
+out:
kfree(out_buf);
return rc;
}
-#endif /* STATS2 */
+
+void
+smb2_cached_lease_break(struct work_struct *work)
+{
+ struct cached_fid *cfid = container_of(work,
+ struct cached_fid, lease_break);
+ mutex_lock(&cfid->fid_mutex);
+ if (cfid->is_valid) {
+ cifs_dbg(FYI, "clear cached root file handle\n");
+ SMB2_close(0, cfid->tcon, cfid->fid->persistent_fid,
+ cfid->fid->volatile_fid);
+ cfid->is_valid = false;
+ }
+ mutex_unlock(&cfid->fid_mutex);
+}
/*
* Open the directory at the root of a share
struct cifs_open_parms oparams;
int rc;
__le16 srch_path = 0; /* Null - since an open of top of share */
- u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
+ u8 oplock = SMB2_OPLOCK_LEVEL_II;
- mutex_lock(&tcon->prfid_mutex);
- if (tcon->valid_root_fid) {
+ mutex_lock(&tcon->crfid.fid_mutex);
+ if (tcon->crfid.is_valid) {
cifs_dbg(FYI, "found a cached root file handle\n");
- memcpy(pfid, tcon->prfid, sizeof(struct cifs_fid));
- mutex_unlock(&tcon->prfid_mutex);
+ memcpy(pfid, tcon->crfid.fid, sizeof(struct cifs_fid));
+ mutex_unlock(&tcon->crfid.fid_mutex);
return 0;
}
rc = SMB2_open(xid, &oparams, &srch_path, &oplock, NULL, NULL, NULL);
if (rc == 0) {
- memcpy(tcon->prfid, pfid, sizeof(struct cifs_fid));
- tcon->valid_root_fid = true;
+ memcpy(tcon->crfid.fid, pfid, sizeof(struct cifs_fid));
+ tcon->crfid.tcon = tcon;
+ tcon->crfid.is_valid = true;
}
- mutex_unlock(&tcon->prfid_mutex);
+ mutex_unlock(&tcon->crfid.fid_mutex);
return rc;
}
if (rc)
return;
-#ifdef CONFIG_CIFS_STATS2
SMB3_request_interfaces(xid, tcon);
-#endif /* STATS2 */
SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid,
FS_ATTRIBUTE_INFORMATION);
struct cifs_open_parms oparms;
struct cifs_fid fid;
- if ((*full_path == 0) && tcon->valid_root_fid)
+ if ((*full_path == 0) && tcon->crfid.is_valid)
return 0;
utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb);
struct smb_rqst *old_rq)
{
struct smb2_sync_hdr *shdr =
- (struct smb2_sync_hdr *)old_rq->rq_iov[1].iov_base;
+ (struct smb2_sync_hdr *)old_rq->rq_iov[0].iov_base;
memset(tr_hdr, 0, sizeof(struct smb2_transform_hdr));
tr_hdr->ProtocolId = SMB2_TRANSFORM_PROTO_NUM;
}
/* Assumes:
- * rqst->rq_iov[0] is rfc1002 length
- * rqst->rq_iov[1] is tranform header
- * rqst->rq_iov[2+] data to be encrypted/decrypted
+ * rqst->rq_iov[0] is transform header
+ * rqst->rq_iov[1+] data to be encrypted/decrypted
*/
static struct scatterlist *
init_sg(struct smb_rqst *rqst, u8 *sign)
{
- unsigned int sg_len = rqst->rq_nvec + rqst->rq_npages;
+ unsigned int sg_len = rqst->rq_nvec + rqst->rq_npages + 1;
unsigned int assoc_data_len = sizeof(struct smb2_transform_hdr) - 20;
struct scatterlist *sg;
unsigned int i;
return NULL;
sg_init_table(sg, sg_len);
- smb2_sg_set_buf(&sg[0], rqst->rq_iov[1].iov_base + 20, assoc_data_len);
- for (i = 1; i < rqst->rq_nvec - 1; i++)
- smb2_sg_set_buf(&sg[i], rqst->rq_iov[i+1].iov_base,
- rqst->rq_iov[i+1].iov_len);
+ smb2_sg_set_buf(&sg[0], rqst->rq_iov[0].iov_base + 20, assoc_data_len);
+ for (i = 1; i < rqst->rq_nvec; i++)
+ smb2_sg_set_buf(&sg[i], rqst->rq_iov[i].iov_base,
+ rqst->rq_iov[i].iov_len);
for (j = 0; i < sg_len - 1; i++, j++) {
unsigned int len, offset;
return 1;
}
/*
- * Encrypt or decrypt @rqst message. @rqst has the following format:
- * iov[0] - rfc1002 length
- * iov[1] - transform header (associate data),
- * iov[2-N] and pages - data to encrypt.
- * On success return encrypted data in iov[2-N] and pages, leave iov[0-1]
+ * Encrypt or decrypt @rqst message. @rqst[0] has the following format:
+ * iov[0] - transform header (associate data),
+ * iov[1-N] - SMB2 header and pages - data to encrypt.
+ * On success return encrypted data in iov[1-N] and pages, leave iov[0]
* untouched.
*/
static int
crypt_message(struct TCP_Server_Info *server, struct smb_rqst *rqst, int enc)
{
struct smb2_transform_hdr *tr_hdr =
- (struct smb2_transform_hdr *)rqst->rq_iov[1].iov_base;
+ (struct smb2_transform_hdr *)rqst->rq_iov[0].iov_base;
unsigned int assoc_data_len = sizeof(struct smb2_transform_hdr) - 20;
int rc = 0;
struct scatterlist *sg;
return rc;
}
-/*
- * This is called from smb_send_rqst. At this point we have the rfc1002
- * header as the first element in the vector.
- */
static int
smb3_init_transform_rq(struct TCP_Server_Info *server, struct smb_rqst *new_rq,
struct smb_rqst *old_rq)
struct page **pages;
struct smb2_transform_hdr *tr_hdr;
unsigned int npages = old_rq->rq_npages;
- unsigned int orig_len = get_rfc1002_length(old_rq->rq_iov[0].iov_base);
+ unsigned int orig_len;
int i;
int rc = -ENOMEM;
goto err_free_pages;
}
- /* Make space for one extra iov to hold the transform header */
iov = kmalloc_array(old_rq->rq_nvec + 1, sizeof(struct kvec),
GFP_KERNEL);
if (!iov)
goto err_free_pages;
- /* copy all iovs from the old except the 1st one (rfc1002 length) */
- memcpy(&iov[2], &old_rq->rq_iov[1],
- sizeof(struct kvec) * (old_rq->rq_nvec - 1));
- /* copy the rfc1002 iov */
- iov[0].iov_base = old_rq->rq_iov[0].iov_base;
- iov[0].iov_len = old_rq->rq_iov[0].iov_len;
+ /* copy all iovs from the old */
+ memcpy(&iov[1], &old_rq->rq_iov[0],
+ sizeof(struct kvec) * old_rq->rq_nvec);
new_rq->rq_iov = iov;
new_rq->rq_nvec = old_rq->rq_nvec + 1;
if (!tr_hdr)
goto err_free_iov;
+ orig_len = smb2_rqst_len(old_rq, false);
+
/* fill the 2nd iov with a transform header */
fill_transform_hdr(tr_hdr, orig_len, old_rq);
- new_rq->rq_iov[1].iov_base = tr_hdr;
- new_rq->rq_iov[1].iov_len = sizeof(struct smb2_transform_hdr);
-
- /* Update rfc1002 header */
- inc_rfc1001_len(new_rq->rq_iov[0].iov_base,
- sizeof(struct smb2_transform_hdr));
+ new_rq->rq_iov[0].iov_base = tr_hdr;
+ new_rq->rq_iov[0].iov_len = sizeof(struct smb2_transform_hdr);
/* copy pages form the old */
for (i = 0; i < npages; i++) {
put_page(rqst->rq_pages[i]);
kfree(rqst->rq_pages);
/* free transform header */
- kfree(rqst->rq_iov[1].iov_base);
+ kfree(rqst->rq_iov[0].iov_base);
kfree(rqst->rq_iov);
}
unsigned int buf_data_size, struct page **pages,
unsigned int npages, unsigned int page_data_size)
{
- struct kvec iov[3];
+ struct kvec iov[2];
struct smb_rqst rqst = {NULL};
int rc;
- iov[0].iov_base = NULL;
- iov[0].iov_len = 0;
- iov[1].iov_base = buf;
- iov[1].iov_len = sizeof(struct smb2_transform_hdr);
- iov[2].iov_base = buf + sizeof(struct smb2_transform_hdr);
- iov[2].iov_len = buf_data_size;
+ iov[0].iov_base = buf;
+ iov[0].iov_len = sizeof(struct smb2_transform_hdr);
+ iov[1].iov_base = buf + sizeof(struct smb2_transform_hdr);
+ iov[1].iov_len = buf_data_size;
rqst.rq_iov = iov;
- rqst.rq_nvec = 3;
+ rqst.rq_nvec = 2;
rqst.rq_pages = pages;
rqst.rq_npages = npages;
rqst.rq_pagesz = PAGE_SIZE;
if (rc)
return rc;
- memmove(buf, iov[2].iov_base, buf_data_size);
+ memmove(buf, iov[1].iov_base, buf_data_size);
server->total_read = buf_data_size + page_data_size;
.set_compression = smb2_set_compression,
.mkdir = smb2_mkdir,
.mkdir_setinfo = smb2_mkdir_setinfo,
+ .posix_mkdir = smb311_posix_mkdir,
.rmdir = smb2_rmdir,
.unlink = smb2_unlink,
.rename = smb2_rename_path,
int
SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)
{
+ struct smb_rqst rqst;
struct smb2_negotiate_req *req;
struct smb2_negotiate_rsp *rsp;
struct kvec iov[1];
iov[0].iov_base = (char *)req;
iov[0].iov_len = total_len;
- rc = smb2_send_recv(xid, ses, iov, 1, &resp_buftype, flags, &rsp_iov);
+ memset(&rqst, 0, sizeof(struct smb_rqst));
+ rqst.rq_iov = iov;
+ rqst.rq_nvec = 1;
+
+ rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, &rsp_iov);
cifs_small_buf_release(req);
rsp = (struct smb2_negotiate_rsp *)rsp_iov.iov_base;
/*
req->PreviousSessionId = sess_data->previous_session;
req->Flags = 0; /* MBZ */
- /* to enable echos and oplocks */
- req->sync_hdr.CreditRequest = cpu_to_le16(3);
+
+ /* enough to enable echos and oplocks and one max size write */
+ req->sync_hdr.CreditRequest = cpu_to_le16(130);
/* only one of SMB2 signing flags may be set in SMB2 request */
if (server->sign)
SMB2_sess_sendreceive(struct SMB2_sess_data *sess_data)
{
int rc;
+ struct smb_rqst rqst;
struct smb2_sess_setup_req *req = sess_data->iov[0].iov_base;
struct kvec rsp_iov = { NULL, 0 };
cpu_to_le16(sizeof(struct smb2_sess_setup_req) - 1 /* pad */);
req->SecurityBufferLength = cpu_to_le16(sess_data->iov[1].iov_len);
- /* BB add code to build os and lm fields */
+ memset(&rqst, 0, sizeof(struct smb_rqst));
+ rqst.rq_iov = sess_data->iov;
+ rqst.rq_nvec = 2;
- rc = smb2_send_recv(sess_data->xid, sess_data->ses,
- sess_data->iov, 2,
+ /* BB add code to build os and lm fields */
+ rc = cifs_send_recv(sess_data->xid, sess_data->ses,
+ &rqst,
&sess_data->buf0_type,
CIFS_LOG_ERROR | CIFS_NEG_OP, &rsp_iov);
cifs_small_buf_release(sess_data->iov[0].iov_base);
int
SMB2_logoff(const unsigned int xid, struct cifs_ses *ses)
{
+ struct smb_rqst rqst;
struct smb2_logoff_req *req; /* response is also trivial struct */
int rc = 0;
struct TCP_Server_Info *server;
iov[0].iov_base = (char *)req;
iov[0].iov_len = total_len;
- rc = smb2_send_recv(xid, ses, iov, 1, &resp_buf_type, flags, &rsp_iov);
+ memset(&rqst, 0, sizeof(struct smb_rqst));
+ rqst.rq_iov = iov;
+ rqst.rq_nvec = 1;
+
+ rc = cifs_send_recv(xid, ses, &rqst, &resp_buf_type, flags, &rsp_iov);
cifs_small_buf_release(req);
/*
* No tcon so can't do
SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree,
struct cifs_tcon *tcon, const struct nls_table *cp)
{
+ struct smb_rqst rqst;
struct smb2_tree_connect_req *req;
struct smb2_tree_connect_rsp *rsp = NULL;
struct kvec iov[2];
!smb3_encryption_required(tcon))
req->sync_hdr.Flags |= SMB2_FLAGS_SIGNED;
- rc = smb2_send_recv(xid, ses, iov, 2, &resp_buftype, flags, &rsp_iov);
+ memset(&rqst, 0, sizeof(struct smb_rqst));
+ rqst.rq_iov = iov;
+ rqst.rq_nvec = 2;
+
+ rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, &rsp_iov);
cifs_small_buf_release(req);
rsp = (struct smb2_tree_connect_rsp *)rsp_iov.iov_base;
int
SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon)
{
+ struct smb_rqst rqst;
struct smb2_tree_disconnect_req *req; /* response is trivial */
int rc = 0;
struct cifs_ses *ses = tcon->ses;
iov[0].iov_base = (char *)req;
iov[0].iov_len = total_len;
- rc = smb2_send_recv(xid, ses, iov, 1, &resp_buf_type, flags, &rsp_iov);
+ memset(&rqst, 0, sizeof(struct smb_rqst));
+ rqst.rq_iov = iov;
+ rqst.rq_nvec = 1;
+
+ rc = cifs_send_recv(xid, ses, &rqst, &resp_buf_type, flags, &rsp_iov);
cifs_small_buf_release(req);
if (rc)
cifs_stats_fail_inc(tcon, SMB2_TREE_DISCONNECT_HE);
return 0;
}
+#ifdef CONFIG_CIFS_SMB311
+int smb311_posix_mkdir(const unsigned int xid, struct inode *inode,
+ umode_t mode, struct cifs_tcon *tcon,
+ const char *full_path,
+ struct cifs_sb_info *cifs_sb)
+{
+ struct smb_rqst rqst;
+ struct smb2_create_req *req;
+ struct smb2_create_rsp *rsp;
+ struct TCP_Server_Info *server;
+ struct cifs_ses *ses = tcon->ses;
+ struct kvec iov[3]; /* make sure at least one for each open context */
+ struct kvec rsp_iov = {NULL, 0};
+ int resp_buftype;
+ int uni_path_len;
+ __le16 *copy_path = NULL;
+ int copy_size;
+ int rc = 0;
+ unsigned int n_iov = 2;
+ __u32 file_attributes = 0;
+ char *pc_buf = NULL;
+ int flags = 0;
+ unsigned int total_len;
+ __le16 *path = cifs_convert_path_to_utf16(full_path, cifs_sb);
+
+ if (!path)
+ return -ENOMEM;
+
+ cifs_dbg(FYI, "mkdir\n");
+
+ if (ses && (ses->server))
+ server = ses->server;
+ else
+ return -EIO;
+
+ rc = smb2_plain_req_init(SMB2_CREATE, tcon, (void **) &req, &total_len);
+
+ if (rc)
+ return rc;
+
+ if (smb3_encryption_required(tcon))
+ flags |= CIFS_TRANSFORM_REQ;
+
+
+ req->ImpersonationLevel = IL_IMPERSONATION;
+ req->DesiredAccess = cpu_to_le32(FILE_WRITE_ATTRIBUTES);
+ /* File attributes ignored on open (used in create though) */
+ req->FileAttributes = cpu_to_le32(file_attributes);
+ req->ShareAccess = FILE_SHARE_ALL_LE;
+ req->CreateDisposition = cpu_to_le32(FILE_CREATE);
+ req->CreateOptions = cpu_to_le32(CREATE_NOT_FILE);
+
+ iov[0].iov_base = (char *)req;
+ /* -1 since last byte is buf[0] which is sent below (path) */
+ iov[0].iov_len = total_len - 1;
+
+ req->NameOffset = cpu_to_le16(sizeof(struct smb2_create_req));
+
+ /* [MS-SMB2] 2.2.13 NameOffset:
+ * If SMB2_FLAGS_DFS_OPERATIONS is set in the Flags field of
+ * the SMB2 header, the file name includes a prefix that will
+ * be processed during DFS name normalization as specified in
+ * section 3.3.5.9. Otherwise, the file name is relative to
+ * the share that is identified by the TreeId in the SMB2
+ * header.
+ */
+ if (tcon->share_flags & SHI1005_FLAGS_DFS) {
+ int name_len;
+
+ req->sync_hdr.Flags |= SMB2_FLAGS_DFS_OPERATIONS;
+ rc = alloc_path_with_tree_prefix(©_path, ©_size,
+ &name_len,
+ tcon->treeName, path);
+ if (rc) {
+ cifs_small_buf_release(req);
+ return rc;
+ }
+ req->NameLength = cpu_to_le16(name_len * 2);
+ uni_path_len = copy_size;
+ path = copy_path;
+ } else {
+ uni_path_len = (2 * UniStrnlen((wchar_t *)path, PATH_MAX)) + 2;
+ /* MUST set path len (NameLength) to 0 opening root of share */
+ req->NameLength = cpu_to_le16(uni_path_len - 2);
+ if (uni_path_len % 8 != 0) {
+ copy_size = roundup(uni_path_len, 8);
+ copy_path = kzalloc(copy_size, GFP_KERNEL);
+ if (!copy_path) {
+ cifs_small_buf_release(req);
+ return -ENOMEM;
+ }
+ memcpy((char *)copy_path, (const char *)path,
+ uni_path_len);
+ uni_path_len = copy_size;
+ path = copy_path;
+ }
+ }
+
+ iov[1].iov_len = uni_path_len;
+ iov[1].iov_base = path;
+ req->RequestedOplockLevel = SMB2_OPLOCK_LEVEL_NONE;
+
+ if (tcon->posix_extensions) {
+ if (n_iov > 2) {
+ struct create_context *ccontext =
+ (struct create_context *)iov[n_iov-1].iov_base;
+ ccontext->Next =
+ cpu_to_le32(iov[n_iov-1].iov_len);
+ }
+
+ rc = add_posix_context(iov, &n_iov, mode);
+ if (rc) {
+ cifs_small_buf_release(req);
+ kfree(copy_path);
+ return rc;
+ }
+ pc_buf = iov[n_iov-1].iov_base;
+ }
+
+
+ memset(&rqst, 0, sizeof(struct smb_rqst));
+ rqst.rq_iov = iov;
+ rqst.rq_nvec = n_iov;
+
+ rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags,
+ &rsp_iov);
+
+ cifs_small_buf_release(req);
+ rsp = (struct smb2_create_rsp *)rsp_iov.iov_base;
+
+ if (rc != 0) {
+ cifs_stats_fail_inc(tcon, SMB2_CREATE_HE);
+ trace_smb3_posix_mkdir_err(xid, tcon->tid, ses->Suid,
+ CREATE_NOT_FILE, FILE_WRITE_ATTRIBUTES, rc);
+ goto smb311_mkdir_exit;
+ } else
+ trace_smb3_posix_mkdir_done(xid, rsp->PersistentFileId, tcon->tid,
+ ses->Suid, CREATE_NOT_FILE,
+ FILE_WRITE_ATTRIBUTES);
+
+ SMB2_close(xid, tcon, rsp->PersistentFileId, rsp->VolatileFileId);
+
+ /* Eventually save off posix specific response info and timestaps */
+
+smb311_mkdir_exit:
+ kfree(copy_path);
+ kfree(pc_buf);
+ free_rsp_buf(resp_buftype, rsp);
+ return rc;
+
+}
+#endif /* SMB311 */
+
int
SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
__u8 *oplock, struct smb2_file_all_info *buf,
struct kvec *err_iov, int *buftype)
{
+ struct smb_rqst rqst;
struct smb2_create_req *req;
struct smb2_create_rsp *rsp;
struct TCP_Server_Info *server;
}
#endif /* SMB311 */
- rc = smb2_send_recv(xid, ses, iov, n_iov, &resp_buftype, flags,
+ memset(&rqst, 0, sizeof(struct smb_rqst));
+ rqst.rq_iov = iov;
+ rqst.rq_nvec = n_iov;
+
+ rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags,
&rsp_iov);
cifs_small_buf_release(req);
rsp = (struct smb2_create_rsp *)rsp_iov.iov_base;
char *in_data, u32 indatalen,
char **out_data, u32 *plen /* returned data len */)
{
+ struct smb_rqst rqst;
struct smb2_ioctl_req *req;
struct smb2_ioctl_rsp *rsp;
struct cifs_ses *ses;
if (opcode == FSCTL_VALIDATE_NEGOTIATE_INFO)
req->sync_hdr.Flags |= SMB2_FLAGS_SIGNED;
- rc = smb2_send_recv(xid, ses, iov, n_iov, &resp_buftype, flags,
+ memset(&rqst, 0, sizeof(struct smb_rqst));
+ rqst.rq_iov = iov;
+ rqst.rq_nvec = n_iov;
+
+ rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags,
&rsp_iov);
cifs_small_buf_release(req);
rsp = (struct smb2_ioctl_rsp *)rsp_iov.iov_base;
SMB2_close_flags(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_fid, u64 volatile_fid, int flags)
{
+ struct smb_rqst rqst;
struct smb2_close_req *req;
struct smb2_close_rsp *rsp;
struct cifs_ses *ses = tcon->ses;
iov[0].iov_base = (char *)req;
iov[0].iov_len = total_len;
- rc = smb2_send_recv(xid, ses, iov, 1, &resp_buftype, flags, &rsp_iov);
+ memset(&rqst, 0, sizeof(struct smb_rqst));
+ rqst.rq_iov = iov;
+ rqst.rq_nvec = 1;
+
+ rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, &rsp_iov);
cifs_small_buf_release(req);
rsp = (struct smb2_close_rsp *)rsp_iov.iov_base;
u32 additional_info, size_t output_len, size_t min_len, void **data,
u32 *dlen)
{
+ struct smb_rqst rqst;
struct smb2_query_info_req *req;
struct smb2_query_info_rsp *rsp = NULL;
struct kvec iov[2];
/* 1 for Buffer */
iov[0].iov_len = total_len - 1;
- rc = smb2_send_recv(xid, ses, iov, 1, &resp_buftype, flags, &rsp_iov);
+ memset(&rqst, 0, sizeof(struct smb_rqst));
+ rqst.rq_iov = iov;
+ rqst.rq_nvec = 1;
+
+ rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, &rsp_iov);
cifs_small_buf_release(req);
rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base;
{
struct smb2_echo_req *req;
int rc = 0;
- struct kvec iov[2];
+ struct kvec iov[1];
struct smb_rqst rqst = { .rq_iov = iov,
- .rq_nvec = 2 };
+ .rq_nvec = 1 };
unsigned int total_len;
- __be32 rfc1002_marker;
cifs_dbg(FYI, "In echo request\n");
req->sync_hdr.CreditRequest = cpu_to_le16(1);
- iov[0].iov_len = 4;
- rfc1002_marker = cpu_to_be32(total_len);
- iov[0].iov_base = &rfc1002_marker;
- iov[1].iov_len = total_len;
- iov[1].iov_base = (char *)req;
+ iov[0].iov_len = total_len;
+ iov[0].iov_base = (char *)req;
rc = cifs_call_async(server, &rqst, NULL, smb2_echo_callback, NULL,
server, CIFS_ECHO_OP);
SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
u64 volatile_fid)
{
+ struct smb_rqst rqst;
struct smb2_flush_req *req;
struct cifs_ses *ses = tcon->ses;
struct kvec iov[1];
iov[0].iov_base = (char *)req;
iov[0].iov_len = total_len;
- rc = smb2_send_recv(xid, ses, iov, 1, &resp_buftype, flags, &rsp_iov);
+ memset(&rqst, 0, sizeof(struct smb_rqst));
+ rqst.rq_iov = iov;
+ rqst.rq_nvec = 1;
+
+ rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, &rsp_iov);
cifs_small_buf_release(req);
if (rc != 0) {
struct smb2_sync_hdr *shdr;
struct cifs_io_parms io_parms;
struct smb_rqst rqst = { .rq_iov = rdata->iov,
- .rq_nvec = 2 };
+ .rq_nvec = 1 };
struct TCP_Server_Info *server;
unsigned int total_len;
- __be32 req_len;
cifs_dbg(FYI, "%s: offset=%llu bytes=%u\n",
__func__, rdata->offset, rdata->bytes);
if (smb3_encryption_required(io_parms.tcon))
flags |= CIFS_TRANSFORM_REQ;
- req_len = cpu_to_be32(total_len);
-
- rdata->iov[0].iov_base = &req_len;
- rdata->iov[0].iov_len = sizeof(__be32);
- rdata->iov[1].iov_base = buf;
- rdata->iov[1].iov_len = total_len;
+ rdata->iov[0].iov_base = buf;
+ rdata->iov[0].iov_len = total_len;
shdr = (struct smb2_sync_hdr *)buf;
SMB2_read(const unsigned int xid, struct cifs_io_parms *io_parms,
unsigned int *nbytes, char **buf, int *buf_type)
{
+ struct smb_rqst rqst;
int resp_buftype, rc = -EACCES;
struct smb2_read_plain_req *req = NULL;
struct smb2_read_rsp *rsp = NULL;
iov[0].iov_base = (char *)req;
iov[0].iov_len = total_len;
- rc = smb2_send_recv(xid, ses, iov, 1, &resp_buftype, flags, &rsp_iov);
+ memset(&rqst, 0, sizeof(struct smb_rqst));
+ rqst.rq_iov = iov;
+ rqst.rq_nvec = 1;
+
+ rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, &rsp_iov);
cifs_small_buf_release(req);
rsp = (struct smb2_read_rsp *)rsp_iov.iov_base;
struct smb2_sync_hdr *shdr;
struct cifs_tcon *tcon = tlink_tcon(wdata->cfile->tlink);
struct TCP_Server_Info *server = tcon->ses->server;
- struct kvec iov[2];
+ struct kvec iov[1];
struct smb_rqst rqst = { };
unsigned int total_len;
- __be32 rfc1002_marker;
rc = smb2_plain_req_init(SMB2_WRITE, tcon, (void **) &req, &total_len);
if (rc) {
v1->length = cpu_to_le32(wdata->mr->mr->length);
}
#endif
- /* 4 for rfc1002 length field and 1 for Buffer */
- iov[0].iov_len = 4;
- rfc1002_marker = cpu_to_be32(total_len - 1 + wdata->bytes);
- iov[0].iov_base = &rfc1002_marker;
- iov[1].iov_len = total_len - 1;
- iov[1].iov_base = (char *)req;
+ iov[0].iov_len = total_len - 1;
+ iov[0].iov_base = (char *)req;
rqst.rq_iov = iov;
- rqst.rq_nvec = 2;
+ rqst.rq_nvec = 1;
rqst.rq_pages = wdata->pages;
rqst.rq_offset = wdata->page_offset;
rqst.rq_npages = wdata->nr_pages;
rqst.rq_tailsz = wdata->tailsz;
#ifdef CONFIG_CIFS_SMB_DIRECT
if (wdata->mr) {
- iov[1].iov_len += sizeof(struct smbd_buffer_descriptor_v1);
+ iov[0].iov_len += sizeof(struct smbd_buffer_descriptor_v1);
rqst.rq_npages = 0;
}
#endif
SMB2_write(const unsigned int xid, struct cifs_io_parms *io_parms,
unsigned int *nbytes, struct kvec *iov, int n_vec)
{
+ struct smb_rqst rqst;
int rc = 0;
struct smb2_write_req *req = NULL;
struct smb2_write_rsp *rsp = NULL;
/* 1 for Buffer */
iov[0].iov_len = total_len - 1;
- rc = smb2_send_recv(xid, io_parms->tcon->ses, iov, n_vec + 1,
+ memset(&rqst, 0, sizeof(struct smb_rqst));
+ rqst.rq_iov = iov;
+ rqst.rq_nvec = n_vec + 1;
+
+ rc = cifs_send_recv(xid, io_parms->tcon->ses, &rqst,
&resp_buftype, flags, &rsp_iov);
cifs_small_buf_release(req);
rsp = (struct smb2_write_rsp *)rsp_iov.iov_base;
u64 persistent_fid, u64 volatile_fid, int index,
struct cifs_search_info *srch_inf)
{
+ struct smb_rqst rqst;
struct smb2_query_directory_req *req;
struct smb2_query_directory_rsp *rsp = NULL;
struct kvec iov[2];
iov[1].iov_base = (char *)(req->Buffer);
iov[1].iov_len = len;
- rc = smb2_send_recv(xid, ses, iov, 2, &resp_buftype, flags, &rsp_iov);
+ memset(&rqst, 0, sizeof(struct smb_rqst));
+ rqst.rq_iov = iov;
+ rqst.rq_nvec = 2;
+
+ rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, &rsp_iov);
cifs_small_buf_release(req);
rsp = (struct smb2_query_directory_rsp *)rsp_iov.iov_base;
u8 info_type, u32 additional_info, unsigned int num,
void **data, unsigned int *size)
{
+ struct smb_rqst rqst;
struct smb2_set_info_req *req;
struct smb2_set_info_rsp *rsp = NULL;
struct kvec *iov;
iov[i].iov_len = size[i];
}
- rc = smb2_send_recv(xid, ses, iov, num, &resp_buftype, flags,
+ memset(&rqst, 0, sizeof(struct smb_rqst));
+ rqst.rq_iov = iov;
+ rqst.rq_nvec = num;
+
+ rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags,
&rsp_iov);
cifs_small_buf_release(req);
rsp = (struct smb2_set_info_rsp *)rsp_iov.iov_base;
const u64 persistent_fid, const u64 volatile_fid,
__u8 oplock_level)
{
+ struct smb_rqst rqst;
int rc;
struct smb2_oplock_break *req = NULL;
struct cifs_ses *ses = tcon->ses;
iov[0].iov_base = (char *)req;
iov[0].iov_len = total_len;
- rc = smb2_send_recv(xid, ses, iov, 1, &resp_buf_type, flags, &rsp_iov);
+ memset(&rqst, 0, sizeof(struct smb_rqst));
+ rqst.rq_iov = iov;
+ rqst.rq_nvec = 1;
+
+ rc = cifs_send_recv(xid, ses, &rqst, &resp_buf_type, flags, &rsp_iov);
cifs_small_buf_release(req);
if (rc) {
SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_fid, u64 volatile_fid, struct kstatfs *fsdata)
{
+ struct smb_rqst rqst;
struct smb2_query_info_rsp *rsp = NULL;
struct kvec iov;
struct kvec rsp_iov;
if (smb3_encryption_required(tcon))
flags |= CIFS_TRANSFORM_REQ;
- rc = smb2_send_recv(xid, ses, &iov, 1, &resp_buftype, flags, &rsp_iov);
+ memset(&rqst, 0, sizeof(struct smb_rqst));
+ rqst.rq_iov = &iov;
+ rqst.rq_nvec = 1;
+
+ rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, &rsp_iov);
cifs_small_buf_release(iov.iov_base);
if (rc) {
cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE);
SMB2_QFS_attr(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_fid, u64 volatile_fid, int level)
{
+ struct smb_rqst rqst;
struct smb2_query_info_rsp *rsp = NULL;
struct kvec iov;
struct kvec rsp_iov;
if (smb3_encryption_required(tcon))
flags |= CIFS_TRANSFORM_REQ;
- rc = smb2_send_recv(xid, ses, &iov, 1, &resp_buftype, flags, &rsp_iov);
+ memset(&rqst, 0, sizeof(struct smb_rqst));
+ rqst.rq_iov = &iov;
+ rqst.rq_nvec = 1;
+
+ rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, &rsp_iov);
cifs_small_buf_release(iov.iov_base);
if (rc) {
cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE);
const __u64 persist_fid, const __u64 volatile_fid, const __u32 pid,
const __u32 num_lock, struct smb2_lock_element *buf)
{
+ struct smb_rqst rqst;
int rc = 0;
struct smb2_lock_req *req = NULL;
struct kvec iov[2];
iov[1].iov_len = count;
cifs_stats_inc(&tcon->stats.cifs_stats.num_locks);
- rc = smb2_send_recv(xid, tcon->ses, iov, 2, &resp_buf_type, flags,
+
+ memset(&rqst, 0, sizeof(struct smb_rqst));
+ rqst.rq_iov = iov;
+ rqst.rq_nvec = 2;
+
+ rc = cifs_send_recv(xid, tcon->ses, &rqst, &resp_buf_type, flags,
&rsp_iov);
cifs_small_buf_release(req);
if (rc) {
SMB2_lease_break(const unsigned int xid, struct cifs_tcon *tcon,
__u8 *lease_key, const __le32 lease_state)
{
+ struct smb_rqst rqst;
int rc;
struct smb2_lease_ack *req = NULL;
struct cifs_ses *ses = tcon->ses;
iov[0].iov_base = (char *)req;
iov[0].iov_len = total_len;
- rc = smb2_send_recv(xid, ses, iov, 1, &resp_buf_type, flags, &rsp_iov);
+ memset(&rqst, 0, sizeof(struct smb_rqst));
+ rqst.rq_iov = iov;
+ rqst.rq_nvec = 1;
+
+ rc = cifs_send_recv(xid, ses, &rqst, &resp_buf_type, flags, &rsp_iov);
cifs_small_buf_release(req);
if (rc) {
__le16 Dialect; /* Dialect in use for the connection */
} __packed;
-#define RSS_CAPABLE 0x00000001
-#define RDMA_CAPABLE 0x00000002
+#define RSS_CAPABLE cpu_to_le32(0x00000001)
+#define RDMA_CAPABLE cpu_to_le32(0x00000002)
+
+#define INTERNETWORK cpu_to_le16(0x0002)
+#define INTERNETWORKV6 cpu_to_le16(0x0017)
struct network_interface_info_ioctl_rsp {
__le32 Next; /* next interface. zero if this is last one */
__le32 Capability; /* RSS or RDMA Capable */
__le32 Reserved;
__le64 LinkSpeed;
- char SockAddr_Storage[128];
+ __le16 Family;
+ __u8 Buffer[126];
+} __packed;
+
+struct iface_info_ipv4 {
+ __be16 Port;
+ __be32 IPv4Address;
+ __be64 Reserved;
+} __packed;
+
+struct iface_info_ipv6 {
+ __be16 Port;
+ __be32 FlowInfo;
+ __u8 IPv6Address[16];
+ __be32 ScopeId;
} __packed;
#define NO_FILE_ID 0xFFFFFFFFFFFFFFFFULL /* general ioctls to srv not to file */
struct cifs_sb_info *cifs_sb, bool set_alloc);
extern int smb2_set_file_info(struct inode *inode, const char *full_path,
FILE_BASIC_INFO *buf, const unsigned int xid);
+extern int smb311_posix_mkdir(const unsigned int xid, struct inode *inode,
+ umode_t mode, struct cifs_tcon *tcon,
+ const char *full_path,
+ struct cifs_sb_info *cifs_sb);
extern int smb2_mkdir(const unsigned int xid, struct cifs_tcon *tcon,
const char *name, struct cifs_sb_info *cifs_sb);
extern void smb2_mkdir_setinfo(struct inode *inode, const char *full_path,
extern int smb2_push_mandatory_locks(struct cifsFileInfo *cfile);
extern void smb2_reconnect_server(struct work_struct *work);
extern int smb3_crypto_aead_allocate(struct TCP_Server_Info *server);
+extern unsigned long
+smb2_rqst_len(struct smb_rqst *rqst, bool skip_rfc1002_marker);
/*
* SMB2 Worker functions - most of protocol specific implementation details
unsigned char smb2_signature[SMB2_HMACSHA256_SIZE];
unsigned char *sigptr = smb2_signature;
struct kvec *iov = rqst->rq_iov;
- int iov_hdr_index = rqst->rq_nvec > 1 ? 1 : 0;
- struct smb2_sync_hdr *shdr =
- (struct smb2_sync_hdr *)iov[iov_hdr_index].iov_base;
+ struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)iov[0].iov_base;
struct cifs_ses *ses;
ses = smb2_find_smb_ses(server, shdr->SessionId);
return rc;
}
- rc = __cifs_calc_signature(rqst, iov_hdr_index, server, sigptr,
+ rc = __cifs_calc_signature(rqst, server, sigptr,
&server->secmech.sdeschmacsha256->shash);
if (!rc)
unsigned char smb3_signature[SMB2_CMACAES_SIZE];
unsigned char *sigptr = smb3_signature;
struct kvec *iov = rqst->rq_iov;
- int iov_hdr_index = rqst->rq_nvec > 1 ? 1 : 0;
- struct smb2_sync_hdr *shdr =
- (struct smb2_sync_hdr *)iov[iov_hdr_index].iov_base;
+ struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)iov[0].iov_base;
struct cifs_ses *ses;
ses = smb2_find_smb_ses(server, shdr->SessionId);
return rc;
}
- rc = __cifs_calc_signature(rqst, iov_hdr_index, server, sigptr,
+ rc = __cifs_calc_signature(rqst, server, sigptr,
&server->secmech.sdesccmacaes->shash);
if (!rc)
{
int rc = 0;
struct smb2_sync_hdr *shdr =
- (struct smb2_sync_hdr *)rqst->rq_iov[1].iov_base;
+ (struct smb2_sync_hdr *)rqst->rq_iov[0].iov_base;
if (!(shdr->Flags & SMB2_FLAGS_SIGNED) ||
server->tcpStatus == CifsNeedNegotiate)
{
int rc;
struct smb2_sync_hdr *shdr =
- (struct smb2_sync_hdr *)rqst->rq_iov[1].iov_base;
+ (struct smb2_sync_hdr *)rqst->rq_iov[0].iov_base;
struct mid_q_entry *mid;
smb2_seq_num_into_buf(ses->server, shdr);
{
int rc;
struct smb2_sync_hdr *shdr =
- (struct smb2_sync_hdr *)rqst->rq_iov[1].iov_base;
+ (struct smb2_sync_hdr *)rqst->rq_iov[0].iov_base;
struct mid_q_entry *mid;
smb2_seq_num_into_buf(server, shdr);
#include "smbdirect.h"
#include "cifs_debug.h"
#include "cifsproto.h"
+#include "smb2proto.h"
static struct smbd_response *get_empty_queue_buffer(
struct smbd_connection *info);
struct kvec vec;
int nvecs;
int size;
- unsigned int buflen = 0, remaining_data_length;
+ unsigned int buflen, remaining_data_length;
int start, i, j;
int max_iov_size =
info->max_send_size - sizeof(struct smbd_data_transfer);
log_write(ERR, "expected the pdu length in 1st iov, but got %zu\n", rqst->rq_iov[0].iov_len);
return -EINVAL;
}
- iov = &rqst->rq_iov[1];
-
- /* total up iov array first */
- for (i = 0; i < rqst->rq_nvec-1; i++) {
- buflen += iov[i].iov_len;
- }
/*
* Add in the page array if there is one. The caller needs to set
* rq_tailsz to PAGE_SIZE when the buffer has multiple pages and
* ends at page boundary
*/
- if (rqst->rq_npages) {
- if (rqst->rq_npages == 1)
- buflen += rqst->rq_tailsz;
- else
- buflen += rqst->rq_pagesz * (rqst->rq_npages - 1) -
- rqst->rq_offset + rqst->rq_tailsz;
- }
+ buflen = smb2_rqst_len(rqst, true);
if (buflen + sizeof(struct smbd_data_transfer) >
info->max_fragmented_send_size) {
goto done;
}
+ iov = &rqst->rq_iov[1];
+
cifs_dbg(FYI, "Sending smb (RDMA): smb_len=%u\n", buflen);
for (i = 0; i < rqst->rq_nvec-1; i++)
dump_smb(iov[i].iov_base, iov[i].iov_len);
TP_ARGS(xid, tid, sesid, create_options, desired_access, rc))
DEFINE_SMB3_OPEN_ERR_EVENT(open_err);
-
+DEFINE_SMB3_OPEN_ERR_EVENT(posix_mkdir_err);
DECLARE_EVENT_CLASS(smb3_open_done_class,
TP_PROTO(unsigned int xid,
TP_ARGS(xid, fid, tid, sesid, create_options, desired_access))
DEFINE_SMB3_OPEN_DONE_EVENT(open_done);
+DEFINE_SMB3_OPEN_DONE_EVENT(posix_mkdir_done);
#endif /* _CIFS_TRACE_H */
return 0;
}
-static unsigned long
-rqst_len(struct smb_rqst *rqst)
+unsigned long
+smb2_rqst_len(struct smb_rqst *rqst, bool skip_rfc1002_marker)
{
unsigned int i;
- struct kvec *iov = rqst->rq_iov;
+ struct kvec *iov;
+ int nvec;
unsigned long buflen = 0;
+ if (skip_rfc1002_marker && rqst->rq_iov[0].iov_len == 4) {
+ iov = &rqst->rq_iov[1];
+ nvec = rqst->rq_nvec - 1;
+ } else {
+ iov = rqst->rq_iov;
+ nvec = rqst->rq_nvec;
+ }
+
/* total up iov array first */
- for (i = 0; i < rqst->rq_nvec; i++)
+ for (i = 0; i < nvec; i++)
buflen += iov[i].iov_len;
/*
}
static int
-__smb_send_rqst(struct TCP_Server_Info *server, struct smb_rqst *rqst)
+__smb_send_rqst(struct TCP_Server_Info *server, int num_rqst,
+ struct smb_rqst *rqst)
{
- int rc;
- struct kvec *iov = rqst->rq_iov;
- int n_vec = rqst->rq_nvec;
- unsigned int smb_buf_length = get_rfc1002_length(iov[0].iov_base);
- unsigned long send_length;
- unsigned int i;
+ int rc = 0;
+ struct kvec *iov;
+ int n_vec;
+ unsigned int send_length = 0;
+ unsigned int i, j;
size_t total_len = 0, sent, size;
struct socket *ssocket = server->ssocket;
struct msghdr smb_msg;
int val = 1;
+ __be32 rfc1002_marker;
+
if (cifs_rdma_enabled(server) && server->smbd_conn) {
rc = smbd_send(server->smbd_conn, rqst);
goto smbd_done;
if (ssocket == NULL)
return -ENOTSOCK;
- /* sanity check send length */
- send_length = rqst_len(rqst);
- if (send_length != smb_buf_length + 4) {
- WARN(1, "Send length mismatch(send_length=%lu smb_buf_length=%u)\n",
- send_length, smb_buf_length);
- return -EIO;
- }
-
- if (n_vec < 2)
- return -EIO;
-
- cifs_dbg(FYI, "Sending smb: smb_len=%u\n", smb_buf_length);
- dump_smb(iov[0].iov_base, iov[0].iov_len);
- dump_smb(iov[1].iov_base, iov[1].iov_len);
-
/* cork the socket */
kernel_setsockopt(ssocket, SOL_TCP, TCP_CORK,
(char *)&val, sizeof(val));
- size = 0;
- for (i = 0; i < n_vec; i++)
- size += iov[i].iov_len;
+ for (j = 0; j < num_rqst; j++)
+ send_length += smb2_rqst_len(&rqst[j], true);
+ rfc1002_marker = cpu_to_be32(send_length);
- iov_iter_kvec(&smb_msg.msg_iter, WRITE | ITER_KVEC, iov, n_vec, size);
+ /* Generate a rfc1002 marker for SMB2+ */
+ if (server->vals->header_preamble_size == 0) {
+ struct kvec hiov = {
+ .iov_base = &rfc1002_marker,
+ .iov_len = 4
+ };
+ iov_iter_kvec(&smb_msg.msg_iter, WRITE | ITER_KVEC, &hiov,
+ 1, 4);
+ rc = smb_send_kvec(server, &smb_msg, &sent);
+ if (rc < 0)
+ goto uncork;
- rc = smb_send_kvec(server, &smb_msg, &sent);
- if (rc < 0)
- goto uncork;
+ total_len += sent;
+ send_length += 4;
+ }
- total_len += sent;
+ cifs_dbg(FYI, "Sending smb: smb_len=%u\n", send_length);
- /* now walk the page array and send each page in it */
- for (i = 0; i < rqst->rq_npages; i++) {
- struct bio_vec bvec;
+ for (j = 0; j < num_rqst; j++) {
+ iov = rqst[j].rq_iov;
+ n_vec = rqst[j].rq_nvec;
- bvec.bv_page = rqst->rq_pages[i];
- rqst_page_get_length(rqst, i, &bvec.bv_len, &bvec.bv_offset);
+ size = 0;
+ for (i = 0; i < n_vec; i++) {
+ dump_smb(iov[i].iov_base, iov[i].iov_len);
+ size += iov[i].iov_len;
+ }
+
+ iov_iter_kvec(&smb_msg.msg_iter, WRITE | ITER_KVEC,
+ iov, n_vec, size);
- iov_iter_bvec(&smb_msg.msg_iter, WRITE | ITER_BVEC,
- &bvec, 1, bvec.bv_len);
rc = smb_send_kvec(server, &smb_msg, &sent);
if (rc < 0)
- break;
+ goto uncork;
total_len += sent;
+
+ /* now walk the page array and send each page in it */
+ for (i = 0; i < rqst[j].rq_npages; i++) {
+ struct bio_vec bvec;
+
+ bvec.bv_page = rqst[j].rq_pages[i];
+ rqst_page_get_length(&rqst[j], i, &bvec.bv_len,
+ &bvec.bv_offset);
+
+ iov_iter_bvec(&smb_msg.msg_iter, WRITE | ITER_BVEC,
+ &bvec, 1, bvec.bv_len);
+ rc = smb_send_kvec(server, &smb_msg, &sent);
+ if (rc < 0)
+ break;
+
+ total_len += sent;
+ }
}
uncork:
kernel_setsockopt(ssocket, SOL_TCP, TCP_CORK,
(char *)&val, sizeof(val));
- if ((total_len > 0) && (total_len != smb_buf_length + 4)) {
+ if ((total_len > 0) && (total_len != send_length)) {
cifs_dbg(FYI, "partial send (wanted=%u sent=%zu): terminating session\n",
- smb_buf_length + 4, total_len);
+ send_length, total_len);
/*
* If we have only sent part of an SMB then the next SMB could
* be taken as the remainder of this one. We need to kill the
int rc;
if (!(flags & CIFS_TRANSFORM_REQ))
- return __smb_send_rqst(server, rqst);
+ return __smb_send_rqst(server, 1, rqst);
if (!server->ops->init_transform_rq ||
!server->ops->free_transform_rq) {
if (rc)
return rc;
- rc = __smb_send_rqst(server, &cur_rqst);
+ rc = __smb_send_rqst(server, 1, &cur_rqst);
server->ops->free_transform_rq(&cur_rqst);
return rc;
}
iov[1].iov_base = (char *)smb_buffer + 4;
iov[1].iov_len = smb_buf_length;
- return __smb_send_rqst(server, &rqst);
+ return __smb_send_rqst(server, 1, &rqst);
}
static int
* to the same server. We may make this configurable later or
* use ses->maxReq.
*/
-
rc = wait_for_free_request(ses->server, timeout, optype);
if (rc)
return rc;
#ifdef CONFIG_CIFS_SMB311
if ((ses->status == CifsNew) || (optype & CIFS_NEG_OP))
- smb311_update_preauth_hash(ses, rqst->rq_iov+1,
- rqst->rq_nvec-1);
+ smb311_update_preauth_hash(ses, rqst->rq_iov,
+ rqst->rq_nvec);
#endif
if (timeout == CIFS_ASYNC_OP)
#ifdef CONFIG_CIFS_SMB311
if ((ses->status == CifsNew) || (optype & CIFS_NEG_OP)) {
struct kvec iov = {
- .iov_base = buf,
- .iov_len = midQ->resp_buf_size
+ .iov_base = resp_iov->iov_base,
+ .iov_len = resp_iov->iov_len
};
smb311_update_preauth_hash(ses, &iov, 1);
}
return rc;
}
-/* Like SendReceive2 but iov[0] does not contain an rfc1002 header */
-int
-smb2_send_recv(const unsigned int xid, struct cifs_ses *ses,
- struct kvec *iov, int n_vec, int *resp_buf_type /* ret */,
- const int flags, struct kvec *resp_iov)
-{
- struct smb_rqst rqst;
- struct kvec s_iov[CIFS_MAX_IOV_SIZE], *new_iov;
- int rc;
- int i;
- __u32 count;
- __be32 rfc1002_marker;
-
- if (n_vec + 1 > CIFS_MAX_IOV_SIZE) {
- new_iov = kmalloc_array(n_vec + 1, sizeof(struct kvec),
- GFP_KERNEL);
- if (!new_iov)
- return -ENOMEM;
- } else
- new_iov = s_iov;
-
- /* 1st iov is an RFC1002 Session Message length */
- memcpy(new_iov + 1, iov, (sizeof(struct kvec) * n_vec));
-
- count = 0;
- for (i = 1; i < n_vec + 1; i++)
- count += new_iov[i].iov_len;
-
- rfc1002_marker = cpu_to_be32(count);
-
- new_iov[0].iov_base = &rfc1002_marker;
- new_iov[0].iov_len = 4;
-
- memset(&rqst, 0, sizeof(struct smb_rqst));
- rqst.rq_iov = new_iov;
- rqst.rq_nvec = n_vec + 1;
-
- rc = cifs_send_recv(xid, ses, &rqst, resp_buf_type, flags, resp_iov);
- if (n_vec + 1 > CIFS_MAX_IOV_SIZE)
- kfree(new_iov);
- return rc;
-}
-
int
SendReceive(const unsigned int xid, struct cifs_ses *ses,
struct smb_hdr *in_buf, struct smb_hdr *out_buf,
if (attr->va_size != -1)
inode->i_blocks = (attr->va_size + 511) >> 9;
if (attr->va_atime.tv_sec != -1)
- inode->i_atime = attr->va_atime;
+ inode->i_atime = timespec_to_timespec64(attr->va_atime);
if (attr->va_mtime.tv_sec != -1)
- inode->i_mtime = attr->va_mtime;
+ inode->i_mtime = timespec_to_timespec64(attr->va_mtime);
if (attr->va_ctime.tv_sec != -1)
- inode->i_ctime = attr->va_ctime;
+ inode->i_ctime = timespec_to_timespec64(attr->va_ctime);
}
vattr->va_size = iattr->ia_size;
}
if ( valid & ATTR_ATIME ) {
- vattr->va_atime = iattr->ia_atime;
+ vattr->va_atime = timespec64_to_timespec(iattr->ia_atime);
}
if ( valid & ATTR_MTIME ) {
- vattr->va_mtime = iattr->ia_mtime;
+ vattr->va_mtime = timespec64_to_timespec(iattr->ia_mtime);
}
if ( valid & ATTR_CTIME ) {
- vattr->va_ctime = iattr->ia_ctime;
+ vattr->va_ctime = timespec64_to_timespec(iattr->ia_ctime);
}
}
if (ia_valid & ATTR_GID)
sd_iattr->ia_gid = iattr->ia_gid;
if (ia_valid & ATTR_ATIME)
- sd_iattr->ia_atime = timespec_trunc(iattr->ia_atime,
- inode->i_sb->s_time_gran);
+ sd_iattr->ia_atime = timespec64_trunc(iattr->ia_atime,
+ inode->i_sb->s_time_gran);
if (ia_valid & ATTR_MTIME)
- sd_iattr->ia_mtime = timespec_trunc(iattr->ia_mtime,
- inode->i_sb->s_time_gran);
+ sd_iattr->ia_mtime = timespec64_trunc(iattr->ia_mtime,
+ inode->i_sb->s_time_gran);
if (ia_valid & ATTR_CTIME)
- sd_iattr->ia_ctime = timespec_trunc(iattr->ia_ctime,
- inode->i_sb->s_time_gran);
+ sd_iattr->ia_ctime = timespec64_trunc(iattr->ia_ctime,
+ inode->i_sb->s_time_gran);
if (ia_valid & ATTR_MODE) {
umode_t mode = iattr->ia_mode;
const struct cramfs_inode *cramfs_inode, unsigned int offset)
{
struct inode *inode;
- static struct timespec zerotime;
+ static struct timespec64 zerotime;
inode = iget_locked(sb, cramino(cramfs_inode, offset));
if (!inode)
count = READ_ONCE(ctx->count);
if (count > 0)
- events |= EPOLLIN;
+ events |= (EPOLLIN & eventmask);
if (count == ULLONG_MAX)
events |= EPOLLERR;
if (ULLONG_MAX - 1 > count)
- events |= EPOLLOUT;
+ events |= (EPOLLOUT & eventmask);
return events;
}
return 0;
}
-static __poll_t ep_eventpoll_poll(struct file *file, poll_table *wait)
+static struct wait_queue_head *ep_eventpoll_get_poll_head(struct file *file,
+ __poll_t eventmask)
{
struct eventpoll *ep = file->private_data;
- int depth = 0;
+ return &ep->poll_wait;
+}
- /* Insert inside our poll wait queue */
- poll_wait(file, &ep->poll_wait, wait);
+static __poll_t ep_eventpoll_poll_mask(struct file *file, __poll_t eventmask)
+{
+ struct eventpoll *ep = file->private_data;
+ int depth = 0;
/*
* Proceed to find out if wanted events are really available inside
.show_fdinfo = ep_show_fdinfo,
#endif
.release = ep_eventpoll_release,
- .poll = ep_eventpoll_poll,
+ .get_poll_head = ep_eventpoll_get_poll_head,
+ .poll_mask = ep_eventpoll_poll_mask,
.llseek = noop_llseek,
};
struct ore_io_state **pios)
{
struct ore_io_state *ios;
- struct page **pages;
- struct osd_sg_entry *sgilist;
+ size_t size_ios, size_extra, size_total;
+ void *ios_extra;
+
+ /*
+ * The desired layout looks like this, with the extra_allocation
+ * items pointed at from fields within ios or per_dev:
+
struct __alloc_all_io_state {
struct ore_io_state ios;
struct ore_per_dev_state per_dev[numdevs];
union {
struct osd_sg_entry sglist[sgs_per_dev * numdevs];
struct page *pages[num_par_pages];
- };
- } *_aios;
-
- if (likely(sizeof(*_aios) <= PAGE_SIZE)) {
- _aios = kzalloc(sizeof(*_aios), GFP_KERNEL);
- if (unlikely(!_aios)) {
- ORE_DBGMSG("Failed kzalloc bytes=%zd\n",
- sizeof(*_aios));
+ } extra_allocation;
+ } whole_allocation;
+
+ */
+
+ /* This should never happen, so abort early if it ever does. */
+ if (sgs_per_dev && num_par_pages) {
+ ORE_DBGMSG("Tried to use both pages and sglist\n");
+ *pios = NULL;
+ return -EINVAL;
+ }
+
+ if (numdevs > (INT_MAX - sizeof(*ios)) /
+ sizeof(struct ore_per_dev_state))
+ return -ENOMEM;
+ size_ios = sizeof(*ios) + sizeof(struct ore_per_dev_state) * numdevs;
+
+ if (sgs_per_dev * numdevs > INT_MAX / sizeof(struct osd_sg_entry))
+ return -ENOMEM;
+ if (num_par_pages > INT_MAX / sizeof(struct page *))
+ return -ENOMEM;
+ size_extra = max(sizeof(struct osd_sg_entry) * (sgs_per_dev * numdevs),
+ sizeof(struct page *) * num_par_pages);
+
+ size_total = size_ios + size_extra;
+
+ if (likely(size_total <= PAGE_SIZE)) {
+ ios = kzalloc(size_total, GFP_KERNEL);
+ if (unlikely(!ios)) {
+ ORE_DBGMSG("Failed kzalloc bytes=%zd\n", size_total);
*pios = NULL;
return -ENOMEM;
}
- pages = num_par_pages ? _aios->pages : NULL;
- sgilist = sgs_per_dev ? _aios->sglist : NULL;
- ios = &_aios->ios;
+ ios_extra = (char *)ios + size_ios;
} else {
- struct __alloc_small_io_state {
- struct ore_io_state ios;
- struct ore_per_dev_state per_dev[numdevs];
- } *_aio_small;
- union __extra_part {
- struct osd_sg_entry sglist[sgs_per_dev * numdevs];
- struct page *pages[num_par_pages];
- } *extra_part;
-
- _aio_small = kzalloc(sizeof(*_aio_small), GFP_KERNEL);
- if (unlikely(!_aio_small)) {
+ ios = kzalloc(size_ios, GFP_KERNEL);
+ if (unlikely(!ios)) {
ORE_DBGMSG("Failed alloc first part bytes=%zd\n",
- sizeof(*_aio_small));
+ size_ios);
*pios = NULL;
return -ENOMEM;
}
- extra_part = kzalloc(sizeof(*extra_part), GFP_KERNEL);
- if (unlikely(!extra_part)) {
+ ios_extra = kzalloc(size_extra, GFP_KERNEL);
+ if (unlikely(!ios_extra)) {
ORE_DBGMSG("Failed alloc second part bytes=%zd\n",
- sizeof(*extra_part));
- kfree(_aio_small);
+ size_extra);
+ kfree(ios);
*pios = NULL;
return -ENOMEM;
}
- pages = num_par_pages ? extra_part->pages : NULL;
- sgilist = sgs_per_dev ? extra_part->sglist : NULL;
/* In this case the per_dev[0].sgilist holds the pointer to
* be freed
*/
- ios = &_aio_small->ios;
ios->extra_part_alloc = true;
}
- if (pages) {
- ios->parity_pages = pages;
+ if (num_par_pages) {
+ ios->parity_pages = ios_extra;
ios->max_par_pages = num_par_pages;
}
- if (sgilist) {
+ if (sgs_per_dev) {
+ struct osd_sg_entry *sgilist = ios_extra;
unsigned d;
for (d = 0; d < numdevs; ++d) {
{
struct __stripe_pages_2d *sp2d;
unsigned data_devs = group_width - parity;
+
+ /*
+ * Desired allocation layout is, though when larger than PAGE_SIZE,
+ * each struct __alloc_1p_arrays is separately allocated:
+
struct _alloc_all_bytes {
struct __alloc_stripe_pages_2d {
struct __stripe_pages_2d sp2d;
char page_is_read[data_devs];
} __a1pa[pages_in_unit];
} *_aab;
+
struct __alloc_1p_arrays *__a1pa;
struct __alloc_1p_arrays *__a1pa_end;
- const unsigned sizeof__a1pa = sizeof(_aab->__a1pa[0]);
+
+ */
+
+ char *__a1pa;
+ char *__a1pa_end;
+
+ const size_t sizeof_stripe_pages_2d =
+ sizeof(struct __stripe_pages_2d) +
+ sizeof(struct __1_page_stripe) * pages_in_unit;
+ const size_t sizeof__a1pa =
+ ALIGN(sizeof(struct page *) * (2 * group_width) + data_devs,
+ sizeof(void *));
+ const size_t sizeof__a1pa_arrays = sizeof__a1pa * pages_in_unit;
+ const size_t alloc_total = sizeof_stripe_pages_2d +
+ sizeof__a1pa_arrays;
+
unsigned num_a1pa, alloc_size, i;
/* FIXME: check these numbers in ore_verify_layout */
- BUG_ON(sizeof(_aab->__asp2d) > PAGE_SIZE);
+ BUG_ON(sizeof_stripe_pages_2d > PAGE_SIZE);
BUG_ON(sizeof__a1pa > PAGE_SIZE);
- if (sizeof(*_aab) > PAGE_SIZE) {
- num_a1pa = (PAGE_SIZE - sizeof(_aab->__asp2d)) / sizeof__a1pa;
- alloc_size = sizeof(_aab->__asp2d) + sizeof__a1pa * num_a1pa;
+ /*
+ * If alloc_total would be larger than PAGE_SIZE, only allocate
+ * as many a1pa items as would fill the rest of the page, instead
+ * of the full pages_in_unit count.
+ */
+ if (alloc_total > PAGE_SIZE) {
+ num_a1pa = (PAGE_SIZE - sizeof_stripe_pages_2d) / sizeof__a1pa;
+ alloc_size = sizeof_stripe_pages_2d + sizeof__a1pa * num_a1pa;
} else {
num_a1pa = pages_in_unit;
- alloc_size = sizeof(*_aab);
+ alloc_size = alloc_total;
}
- _aab = kzalloc(alloc_size, GFP_KERNEL);
- if (unlikely(!_aab)) {
+ *psp2d = sp2d = kzalloc(alloc_size, GFP_KERNEL);
+ if (unlikely(!sp2d)) {
ORE_DBGMSG("!! Failed to alloc sp2d size=%d\n", alloc_size);
return -ENOMEM;
}
+ /* From here Just call _sp2d_free */
- sp2d = &_aab->__asp2d.sp2d;
- *psp2d = sp2d; /* From here Just call _sp2d_free */
-
- __a1pa = _aab->__a1pa;
- __a1pa_end = __a1pa + num_a1pa;
+ /* Find start of a1pa area. */
+ __a1pa = (char *)sp2d + sizeof_stripe_pages_2d;
+ /* Find end of the _allocated_ a1pa area. */
+ __a1pa_end = __a1pa + alloc_size;
+ /* Allocate additionally needed a1pa items in PAGE_SIZE chunks. */
for (i = 0; i < pages_in_unit; ++i) {
+ struct __1_page_stripe *stripe = &sp2d->_1p_stripes[i];
+
if (unlikely(__a1pa >= __a1pa_end)) {
num_a1pa = min_t(unsigned, PAGE_SIZE / sizeof__a1pa,
pages_in_unit - i);
+ alloc_size = sizeof__a1pa * num_a1pa;
- __a1pa = kcalloc(num_a1pa, sizeof__a1pa, GFP_KERNEL);
+ __a1pa = kzalloc(alloc_size, GFP_KERNEL);
if (unlikely(!__a1pa)) {
ORE_DBGMSG("!! Failed to _alloc_1p_arrays=%d\n",
num_a1pa);
return -ENOMEM;
}
- __a1pa_end = __a1pa + num_a1pa;
+ __a1pa_end = __a1pa + alloc_size;
/* First *pages is marked for kfree of the buffer */
- sp2d->_1p_stripes[i].alloc = true;
+ stripe->alloc = true;
}
- sp2d->_1p_stripes[i].pages = __a1pa->pages;
- sp2d->_1p_stripes[i].scribble = __a1pa->scribble ;
- sp2d->_1p_stripes[i].page_is_read = __a1pa->page_is_read;
- ++__a1pa;
+ /*
+ * Attach all _lp_stripes pointers to the allocation for
+ * it which was either part of the original PAGE_SIZE
+ * allocation or the subsequent allocation in this loop.
+ */
+ stripe->pages = (void *)__a1pa;
+ stripe->scribble = stripe->pages + group_width;
+ stripe->page_is_read = (char *)stripe->scribble + group_width;
+ __a1pa += sizeof__a1pa;
}
sp2d->parity = parity;
static int __alloc_dev_table(struct exofs_sb_info *sbi, unsigned numdevs,
struct exofs_dev **peds)
{
- struct __alloc_ore_devs_and_exofs_devs {
- /* Twice bigger table: See exofs_init_comps() and comment at
- * exofs_read_lookup_dev_table()
- */
- struct ore_dev *oreds[numdevs * 2 - 1];
- struct exofs_dev eds[numdevs];
- } *aoded;
+ /* Twice bigger table: See exofs_init_comps() and comment at
+ * exofs_read_lookup_dev_table()
+ */
+ const size_t numores = numdevs * 2 - 1;
struct exofs_dev *eds;
unsigned i;
- aoded = kzalloc(sizeof(*aoded), GFP_KERNEL);
- if (unlikely(!aoded)) {
+ sbi->oc.ods = kzalloc(numores * sizeof(struct ore_dev *) +
+ numdevs * sizeof(struct exofs_dev), GFP_KERNEL);
+ if (unlikely(!sbi->oc.ods)) {
EXOFS_ERR("ERROR: failed allocating Device array[%d]\n",
numdevs);
return -ENOMEM;
}
- sbi->oc.ods = aoded->oreds;
- *peds = eds = aoded->eds;
+ /* Start of allocated struct exofs_dev entries */
+ *peds = eds = (void *)sbi->oc.ods[numores];
+ /* Initialize pointers into struct exofs_dev */
for (i = 0; i < numdevs; ++i)
- aoded->oreds[i] = &eds[i].ored;
+ sbi->oc.ods[i] = &eds[i].ored;
return 0;
}
time->tv_nsec = (le32_to_cpu(extra) & EXT4_NSEC_MASK) >> EXT4_EPOCH_BITS;
}
-#define EXT4_INODE_SET_XTIME(xtime, inode, raw_inode) \
-do { \
- (raw_inode)->xtime = cpu_to_le32((inode)->xtime.tv_sec); \
- if (EXT4_FITS_IN_INODE(raw_inode, EXT4_I(inode), xtime ## _extra)) \
- (raw_inode)->xtime ## _extra = \
- ext4_encode_extra_time(&(inode)->xtime); \
+#define EXT4_INODE_SET_XTIME(xtime, inode, raw_inode) \
+do { \
+ (raw_inode)->xtime = cpu_to_le32((inode)->xtime.tv_sec); \
+ if (EXT4_FITS_IN_INODE(raw_inode, EXT4_I(inode), xtime ## _extra)) {\
+ struct timespec ts = timespec64_to_timespec((inode)->xtime); \
+ (raw_inode)->xtime ## _extra = \
+ ext4_encode_extra_time(&ts); \
+ } \
} while (0)
#define EXT4_EINODE_SET_XTIME(xtime, einode, raw_inode) \
ext4_encode_extra_time(&(einode)->xtime); \
} while (0)
-#define EXT4_INODE_GET_XTIME(xtime, inode, raw_inode) \
-do { \
- (inode)->xtime.tv_sec = (signed)le32_to_cpu((raw_inode)->xtime); \
- if (EXT4_FITS_IN_INODE(raw_inode, EXT4_I(inode), xtime ## _extra)) \
- ext4_decode_extra_time(&(inode)->xtime, \
- raw_inode->xtime ## _extra); \
- else \
- (inode)->xtime.tv_nsec = 0; \
+#define EXT4_INODE_GET_XTIME(xtime, inode, raw_inode) \
+do { \
+ (inode)->xtime.tv_sec = (signed)le32_to_cpu((raw_inode)->xtime); \
+ if (EXT4_FITS_IN_INODE(raw_inode, EXT4_I(inode), xtime ## _extra)) { \
+ struct timespec ts = timespec64_to_timespec((inode)->xtime); \
+ ext4_decode_extra_time(&ts, \
+ raw_inode->xtime ## _extra); \
+ (inode)->xtime = timespec_to_timespec64(ts); \
+ } \
+ else \
+ (inode)->xtime.tv_nsec = 0; \
} while (0)
+
#define EXT4_EINODE_GET_XTIME(xtime, einode, raw_inode) \
do { \
if (EXT4_FITS_IN_INODE(raw_inode, einode, xtime)) \
inode->i_ino = ino + group * EXT4_INODES_PER_GROUP(sb);
/* This is the optimal IO size (for stat), not the fs block size */
inode->i_blocks = 0;
- inode->i_mtime = inode->i_atime = inode->i_ctime = ei->i_crtime =
- current_time(inode);
+ inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode);
+ ei->i_crtime = timespec64_to_timespec(inode->i_mtime);
memset(ei->i_data, 0, sizeof(ei->i_data));
ei->i_dir_start_lookup = 0;
};
u8 new_file_type;
int retval;
- struct timespec ctime;
+ struct timespec64 ctime;
if ((ext4_test_inode_flag(new_dir, EXT4_INODE_PROJINHERIT) &&
!projid_eq(EXT4_I(new_dir)->i_projid,
static inline bool f2fs_skip_inode_update(struct inode *inode, int dsync)
{
+ struct timespec ts;
bool ret;
if (dsync) {
i_size_read(inode) & ~PAGE_MASK)
return false;
- if (!timespec_equal(F2FS_I(inode)->i_disk_time, &inode->i_atime))
+ ts = timespec64_to_timespec(inode->i_atime);
+ if (!timespec_equal(F2FS_I(inode)->i_disk_time, &ts))
return false;
- if (!timespec_equal(F2FS_I(inode)->i_disk_time + 1, &inode->i_ctime))
+ ts = timespec64_to_timespec(inode->i_ctime);
+ if (!timespec_equal(F2FS_I(inode)->i_disk_time + 1, &ts))
return false;
- if (!timespec_equal(F2FS_I(inode)->i_disk_time + 2, &inode->i_mtime))
+ ts = timespec64_to_timespec(inode->i_mtime);
+ if (!timespec_equal(F2FS_I(inode)->i_disk_time + 2, &ts))
return false;
if (!timespec_equal(F2FS_I(inode)->i_disk_time + 3,
&F2FS_I(inode)->i_crtime))
if (ia_valid & ATTR_GID)
inode->i_gid = attr->ia_gid;
if (ia_valid & ATTR_ATIME)
- inode->i_atime = timespec_trunc(attr->ia_atime,
- inode->i_sb->s_time_gran);
+ inode->i_atime = timespec64_trunc(attr->ia_atime,
+ inode->i_sb->s_time_gran);
if (ia_valid & ATTR_MTIME)
- inode->i_mtime = timespec_trunc(attr->ia_mtime,
- inode->i_sb->s_time_gran);
+ inode->i_mtime = timespec64_trunc(attr->ia_mtime,
+ inode->i_sb->s_time_gran);
if (ia_valid & ATTR_CTIME)
- inode->i_ctime = timespec_trunc(attr->ia_ctime,
- inode->i_sb->s_time_gran);
+ inode->i_ctime = timespec64_trunc(attr->ia_ctime,
+ inode->i_sb->s_time_gran);
if (ia_valid & ATTR_MODE) {
umode_t mode = attr->ia_mode;
fi->i_crtime.tv_nsec = le32_to_cpu(ri->i_crtime_nsec);
}
- F2FS_I(inode)->i_disk_time[0] = inode->i_atime;
- F2FS_I(inode)->i_disk_time[1] = inode->i_ctime;
- F2FS_I(inode)->i_disk_time[2] = inode->i_mtime;
+ F2FS_I(inode)->i_disk_time[0] = timespec64_to_timespec(inode->i_atime);
+ F2FS_I(inode)->i_disk_time[1] = timespec64_to_timespec(inode->i_ctime);
+ F2FS_I(inode)->i_disk_time[2] = timespec64_to_timespec(inode->i_mtime);
F2FS_I(inode)->i_disk_time[3] = F2FS_I(inode)->i_crtime;
f2fs_put_page(node_page, 1);
if (inode->i_nlink == 0)
clear_inline_node(node_page);
- F2FS_I(inode)->i_disk_time[0] = inode->i_atime;
- F2FS_I(inode)->i_disk_time[1] = inode->i_ctime;
- F2FS_I(inode)->i_disk_time[2] = inode->i_mtime;
+ F2FS_I(inode)->i_disk_time[0] = timespec64_to_timespec(inode->i_atime);
+ F2FS_I(inode)->i_disk_time[1] = timespec64_to_timespec(inode->i_ctime);
+ F2FS_I(inode)->i_disk_time[2] = timespec64_to_timespec(inode->i_mtime);
F2FS_I(inode)->i_disk_time[3] = F2FS_I(inode)->i_crtime;
}
inode->i_ino = ino;
inode->i_blocks = 0;
- inode->i_mtime = inode->i_atime = inode->i_ctime =
- F2FS_I(inode)->i_crtime = current_time(inode);
+ inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode);
+ F2FS_I(inode)->i_crtime = timespec64_to_timespec(inode->i_mtime);
inode->i_generation = sbi->s_next_generation++;
if (S_ISDIR(inode->i_mode))
err = fat_bmap(inode, iblock, &phys, &mapped_blocks, create, false);
if (err)
return err;
+ if (!phys) {
+ fat_fs_error(sb,
+ "invalid FAT chain (i_pos %lld, last_block %llu)",
+ MSDOS_I(inode)->i_pos,
+ (unsigned long long)last_block);
+ return -EIO;
+ }
- BUG_ON(!phys);
BUG_ON(*max_blocks != mapped_blocks);
set_buffer_new(bh_result);
map_bh(bh_result, sb, phys);
/* doesn't deal with root inode */
int fat_fill_inode(struct inode *inode, struct msdos_dir_entry *de)
{
+ struct timespec ts;
struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb);
int error;
inode->i_blocks = ((inode->i_size + (sbi->cluster_size - 1))
& ~((loff_t)sbi->cluster_size - 1)) >> 9;
- fat_time_fat2unix(sbi, &inode->i_mtime, de->time, de->date, 0);
+ fat_time_fat2unix(sbi, &ts, de->time, de->date, 0);
+ inode->i_mtime = timespec_to_timespec64(ts);
if (sbi->options.isvfat) {
- fat_time_fat2unix(sbi, &inode->i_ctime, de->ctime,
+ fat_time_fat2unix(sbi, &ts, de->ctime,
de->cdate, de->ctime_cs);
- fat_time_fat2unix(sbi, &inode->i_atime, 0, de->adate, 0);
+ inode->i_ctime = timespec_to_timespec64(ts);
+ fat_time_fat2unix(sbi, &ts, 0, de->adate, 0);
+ inode->i_atime = timespec_to_timespec64(ts);
} else
inode->i_ctime = inode->i_atime = inode->i_mtime;
static int __fat_write_inode(struct inode *inode, int wait)
{
+ struct timespec ts;
struct super_block *sb = inode->i_sb;
struct msdos_sb_info *sbi = MSDOS_SB(sb);
struct buffer_head *bh;
raw_entry->size = cpu_to_le32(inode->i_size);
raw_entry->attr = fat_make_attrs(inode);
fat_set_start(raw_entry, MSDOS_I(inode)->i_logstart);
- fat_time_unix2fat(sbi, &inode->i_mtime, &raw_entry->time,
+ ts = timespec64_to_timespec(inode->i_mtime);
+ fat_time_unix2fat(sbi, &ts, &raw_entry->time,
&raw_entry->date, NULL);
if (sbi->options.isvfat) {
__le16 atime;
- fat_time_unix2fat(sbi, &inode->i_ctime, &raw_entry->ctime,
+ ts = timespec64_to_timespec(inode->i_ctime);
+ fat_time_unix2fat(sbi, &ts, &raw_entry->ctime,
&raw_entry->cdate, &raw_entry->ctime_cs);
- fat_time_unix2fat(sbi, &inode->i_atime, &atime,
+ ts = timespec64_to_timespec(inode->i_atime);
+ fat_time_unix2fat(sbi, &ts, &atime,
&raw_entry->adate, NULL);
}
spin_unlock(&sbi->inode_hash_lock);
if (err)
return err;
- dir->i_ctime = dir->i_mtime = *ts;
+ dir->i_ctime = dir->i_mtime = timespec_to_timespec64(*ts);
if (IS_DIRSYNC(dir))
(void)fat_sync_inode(dir);
else
struct super_block *sb = dir->i_sb;
struct inode *inode = NULL;
struct fat_slot_info sinfo;
- struct timespec ts;
+ struct timespec64 ts;
+ struct timespec t;
unsigned char msdos_name[MSDOS_NAME];
int err, is_hid;
}
ts = current_time(dir);
- err = msdos_add_entry(dir, msdos_name, 0, is_hid, 0, &ts, &sinfo);
+ t = timespec64_to_timespec(ts);
+ err = msdos_add_entry(dir, msdos_name, 0, is_hid, 0, &t, &sinfo);
if (err)
goto out;
inode = fat_build_inode(sb, sinfo.de, sinfo.i_pos);
struct fat_slot_info sinfo;
struct inode *inode;
unsigned char msdos_name[MSDOS_NAME];
- struct timespec ts;
+ struct timespec64 ts;
+ struct timespec t;
int err, is_hid, cluster;
mutex_lock(&MSDOS_SB(sb)->s_lock);
}
ts = current_time(dir);
- cluster = fat_alloc_new_dir(dir, &ts);
+ t = timespec64_to_timespec(ts);
+ cluster = fat_alloc_new_dir(dir, &t);
if (cluster < 0) {
err = cluster;
goto out;
}
- err = msdos_add_entry(dir, msdos_name, 1, is_hid, cluster, &ts, &sinfo);
+ err = msdos_add_entry(dir, msdos_name, 1, is_hid, cluster, &t, &sinfo);
if (err)
goto out_free;
inc_nlink(dir);
struct msdos_dir_entry *dotdot_de;
struct inode *old_inode, *new_inode;
struct fat_slot_info old_sinfo, sinfo;
- struct timespec ts;
+ struct timespec64 ts;
loff_t new_i_pos;
int err, old_attrs, is_dir, update_dotdot, corrupt = 0;
new_i_pos = MSDOS_I(new_inode)->i_pos;
fat_detach(new_inode);
} else {
+ struct timespec t = timespec64_to_timespec(ts);
err = msdos_add_entry(new_dir, new_name, is_dir, is_hid, 0,
- &ts, &sinfo);
+ &t, &sinfo);
if (err)
goto out;
new_i_pos = sinfo.i_pos;
goto cleanup;
/* update timestamp */
- dir->i_ctime = dir->i_mtime = dir->i_atime = *ts;
+ dir->i_ctime = dir->i_mtime = dir->i_atime = timespec_to_timespec64(*ts);
if (IS_DIRSYNC(dir))
(void)fat_sync_inode(dir);
else
struct super_block *sb = dir->i_sb;
struct inode *inode;
struct fat_slot_info sinfo;
- struct timespec ts;
+ struct timespec64 ts;
+ struct timespec t;
int err;
mutex_lock(&MSDOS_SB(sb)->s_lock);
ts = current_time(dir);
- err = vfat_add_entry(dir, &dentry->d_name, 0, 0, &ts, &sinfo);
+ t = timespec64_to_timespec(ts);
+ err = vfat_add_entry(dir, &dentry->d_name, 0, 0, &t, &sinfo);
if (err)
goto out;
inode_inc_iversion(dir);
struct super_block *sb = dir->i_sb;
struct inode *inode;
struct fat_slot_info sinfo;
- struct timespec ts;
+ struct timespec64 ts;
+ struct timespec t;
int err, cluster;
mutex_lock(&MSDOS_SB(sb)->s_lock);
ts = current_time(dir);
- cluster = fat_alloc_new_dir(dir, &ts);
+ t = timespec64_to_timespec(ts);
+ cluster = fat_alloc_new_dir(dir, &t);
if (cluster < 0) {
err = cluster;
goto out;
}
- err = vfat_add_entry(dir, &dentry->d_name, 1, cluster, &ts, &sinfo);
+ err = vfat_add_entry(dir, &dentry->d_name, 1, cluster, &t, &sinfo);
if (err)
goto out_free;
inode_inc_iversion(dir);
struct msdos_dir_entry *dotdot_de;
struct inode *old_inode, *new_inode;
struct fat_slot_info old_sinfo, sinfo;
- struct timespec ts;
+ struct timespec64 ts;
+ struct timespec t;
loff_t new_i_pos;
int err, is_dir, update_dotdot, corrupt = 0;
struct super_block *sb = old_dir->i_sb;
new_i_pos = MSDOS_I(new_inode)->i_pos;
fat_detach(new_inode);
} else {
+ t = timespec64_to_timespec(ts);
err = vfat_add_entry(new_dir, &new_dentry->d_name, is_dir, 0,
- &ts, &sinfo);
+ &t, &sinfo);
if (err)
goto out;
new_i_pos = sinfo.i_pos;
return;
}
- old_mtime = inode->i_mtime;
+ old_mtime = timespec64_to_timespec(inode->i_mtime);
fuse_change_attributes_common(inode, attr, attr_valid);
oldsize = inode->i_size;
struct buffer_head *bh;
struct gfs2_leaf *leaf;
struct gfs2_dirent *dent;
- struct timespec tv = current_time(inode);
+ struct timespec64 tv = current_time(inode);
error = gfs2_alloc_blocks(ip, &bn, &n, 0, NULL);
if (error)
struct gfs2_inode *ip = GFS2_I(inode);
struct buffer_head *bh = da->bh;
struct gfs2_dirent *dent = da->dent;
- struct timespec tv;
+ struct timespec64 tv;
struct gfs2_leaf *leaf;
int error;
const struct qstr *name = &dentry->d_name;
struct gfs2_dirent *dent, *prev = NULL;
struct buffer_head *bh;
- struct timespec tv = current_time(&dip->i_inode);
+ struct timespec64 tv = current_time(&dip->i_inode);
/* Returns _either_ the entry (if its first in block) or the
previous entry otherwise */
static int gfs2_dinode_in(struct gfs2_inode *ip, const void *buf)
{
const struct gfs2_dinode *str = buf;
- struct timespec atime;
+ struct timespec64 atime;
u16 height, depth;
if (unlikely(ip->i_no_addr != be64_to_cpu(str->di_num.no_addr)))
gfs2_set_inode_blocks(&ip->i_inode, be64_to_cpu(str->di_blocks));
atime.tv_sec = be64_to_cpu(str->di_atime);
atime.tv_nsec = be32_to_cpu(str->di_atime_nsec);
- if (timespec_compare(&ip->i_inode.i_atime, &atime) < 0)
+ if (timespec64_compare(&ip->i_inode.i_atime, &atime) < 0)
ip->i_inode.i_atime = atime;
ip->i_inode.i_mtime.tv_sec = be64_to_cpu(str->di_mtime);
ip->i_inode.i_mtime.tv_nsec = be32_to_cpu(str->di_mtime_nsec);
inode->i_mode &= ~hsb->s_file_umask;
inode->i_mode |= S_IFREG;
inode->i_ctime = inode->i_atime = inode->i_mtime =
- hfs_m_to_utime(rec->file.MdDat);
+ timespec_to_timespec64(hfs_m_to_utime(rec->file.MdDat));
inode->i_op = &hfs_file_inode_operations;
inode->i_fop = &hfs_file_operations;
inode->i_mapping->a_ops = &hfs_aops;
HFS_I(inode)->fs_blocks = 0;
inode->i_mode = S_IFDIR | (S_IRWXUGO & ~hsb->s_dir_umask);
inode->i_ctime = inode->i_atime = inode->i_mtime =
- hfs_m_to_utime(rec->dir.MdDat);
+ timespec_to_timespec64(hfs_m_to_utime(rec->dir.MdDat));
inode->i_op = &hfs_dir_inode_operations;
inode->i_fop = &hfs_dir_operations;
break;
hfsplus_get_perms(inode, &folder->permissions, 1);
set_nlink(inode, 1);
inode->i_size = 2 + be32_to_cpu(folder->valence);
- inode->i_atime = hfsp_mt2ut(folder->access_date);
- inode->i_mtime = hfsp_mt2ut(folder->content_mod_date);
- inode->i_ctime = hfsp_mt2ut(folder->attribute_mod_date);
+ inode->i_atime = timespec_to_timespec64(hfsp_mt2ut(folder->access_date));
+ inode->i_mtime = timespec_to_timespec64(hfsp_mt2ut(folder->content_mod_date));
+ inode->i_ctime = timespec_to_timespec64(hfsp_mt2ut(folder->attribute_mod_date));
HFSPLUS_I(inode)->create_date = folder->create_date;
HFSPLUS_I(inode)->fs_blocks = 0;
if (folder->flags & cpu_to_be16(HFSPLUS_HAS_FOLDER_COUNT)) {
init_special_inode(inode, inode->i_mode,
be32_to_cpu(file->permissions.dev));
}
- inode->i_atime = hfsp_mt2ut(file->access_date);
- inode->i_mtime = hfsp_mt2ut(file->content_mod_date);
- inode->i_ctime = hfsp_mt2ut(file->attribute_mod_date);
+ inode->i_atime = timespec_to_timespec64(hfsp_mt2ut(file->access_date));
+ inode->i_mtime = timespec_to_timespec64(hfsp_mt2ut(file->content_mod_date));
+ inode->i_ctime = timespec_to_timespec64(hfsp_mt2ut(file->attribute_mod_date));
HFSPLUS_I(inode)->create_date = file->create_date;
} else {
pr_err("bad catalog entry used to create inode\n");
set_nlink(ino, st.nlink);
i_uid_write(ino, st.uid);
i_gid_write(ino, st.gid);
- ino->i_atime = st.atime;
- ino->i_mtime = st.mtime;
- ino->i_ctime = st.ctime;
+ ino->i_atime = timespec_to_timespec64(st.atime);
+ ino->i_mtime = timespec_to_timespec64(st.mtime);
+ ino->i_ctime = timespec_to_timespec64(st.ctime);
ino->i_size = st.size;
ino->i_blocks = st.blocks;
return 0;
}
if (attr->ia_valid & ATTR_ATIME) {
attrs.ia_valid |= HOSTFS_ATTR_ATIME;
- attrs.ia_atime = attr->ia_atime;
+ attrs.ia_atime = timespec64_to_timespec(attr->ia_atime);
}
if (attr->ia_valid & ATTR_MTIME) {
attrs.ia_valid |= HOSTFS_ATTR_MTIME;
- attrs.ia_mtime = attr->ia_mtime;
+ attrs.ia_mtime = timespec64_to_timespec(attr->ia_mtime);
}
if (attr->ia_valid & ATTR_CTIME) {
attrs.ia_valid |= HOSTFS_ATTR_CTIME;
- attrs.ia_ctime = attr->ia_ctime;
+ attrs.ia_ctime = timespec64_to_timespec(attr->ia_ctime);
}
if (attr->ia_valid & ATTR_ATIME_SET) {
attrs.ia_valid |= HOSTFS_ATTR_ATIME_SET;
if (upperdentry) {
struct inode *realinode = d_inode(upperdentry);
- if ((!timespec_equal(&inode->i_mtime, &realinode->i_mtime) ||
- !timespec_equal(&inode->i_ctime, &realinode->i_ctime))) {
+ if ((!timespec64_equal(&inode->i_mtime, &realinode->i_mtime) ||
+ !timespec64_equal(&inode->i_ctime, &realinode->i_ctime))) {
inode->i_mtime = realinode->i_mtime;
inode->i_ctime = realinode->i_ctime;
}
/*
* Is mtime younger than atime? If yes, update atime:
*/
- if (timespec_compare(&inode->i_mtime, &inode->i_atime) >= 0)
+ if (timespec64_compare(&inode->i_mtime, &inode->i_atime) >= 0)
return 1;
/*
* Is ctime younger than atime? If yes, update atime:
*/
- if (timespec_compare(&inode->i_ctime, &inode->i_atime) >= 0)
+ if (timespec64_compare(&inode->i_ctime, &inode->i_atime) >= 0)
return 1;
/*
return 0;
}
-int generic_update_time(struct inode *inode, struct timespec *time, int flags)
+int generic_update_time(struct inode *inode, struct timespec64 *time, int flags)
{
int iflags = I_DIRTY_TIME;
bool dirty = false;
* This does the actual work of updating an inodes time or version. Must have
* had called mnt_want_write() before calling this.
*/
-static int update_time(struct inode *inode, struct timespec *time, int flags)
+static int update_time(struct inode *inode, struct timespec64 *time, int flags)
{
- int (*update_time)(struct inode *, struct timespec *, int);
+ int (*update_time)(struct inode *, struct timespec64 *, int);
update_time = inode->i_op->update_time ? inode->i_op->update_time :
generic_update_time;
bool rcu)
{
struct vfsmount *mnt = path->mnt;
- struct timespec now;
+ struct timespec64 now;
if (inode->i_flags & S_NOATIME)
return false;
now = current_time(inode);
- if (!relatime_need_update(path, inode, now, rcu))
+ if (!relatime_need_update(path, inode, timespec64_to_timespec(now), rcu))
return false;
- if (timespec_equal(&inode->i_atime, &now))
+ if (timespec64_equal(&inode->i_atime, &now))
return false;
return true;
{
struct vfsmount *mnt = path->mnt;
struct inode *inode = d_inode(path->dentry);
- struct timespec now;
+ struct timespec64 now;
if (!__atime_needs_update(path, inode, false))
return;
int file_update_time(struct file *file)
{
struct inode *inode = file_inode(file);
- struct timespec now;
+ struct timespec64 now;
int sync_it = 0;
int ret;
return 0;
now = current_time(inode);
- if (!timespec_equal(&inode->i_mtime, &now))
+ if (!timespec64_equal(&inode->i_mtime, &now))
sync_it = S_MTIME;
- if (!timespec_equal(&inode->i_ctime, &now))
+ if (!timespec64_equal(&inode->i_ctime, &now))
sync_it |= S_CTIME;
if (IS_I_VERSION(inode) && inode_iversion_need_inc(inode))
}
EXPORT_SYMBOL(inode_nohighmem);
+/**
+ * timespec64_trunc - Truncate timespec64 to a granularity
+ * @t: Timespec64
+ * @gran: Granularity in ns.
+ *
+ * Truncate a timespec64 to a granularity. Always rounds down. gran must
+ * not be 0 nor greater than a second (NSEC_PER_SEC, or 10^9 ns).
+ */
+struct timespec64 timespec64_trunc(struct timespec64 t, unsigned gran)
+{
+ /* Avoid division in the common cases 1 ns and 1 s. */
+ if (gran == 1) {
+ /* nothing */
+ } else if (gran == NSEC_PER_SEC) {
+ t.tv_nsec = 0;
+ } else if (gran > 1 && gran < NSEC_PER_SEC) {
+ t.tv_nsec -= t.tv_nsec % gran;
+ } else {
+ WARN(1, "illegal file time granularity: %u", gran);
+ }
+ return t;
+}
+EXPORT_SYMBOL(timespec64_trunc);
+
/**
* current_time - Return FS time
* @inode: inode.
* Note that inode and inode->sb cannot be NULL.
* Otherwise, the function warns and returns time without truncation.
*/
-struct timespec current_time(struct inode *inode)
+struct timespec64 current_time(struct inode *inode)
{
- struct timespec now = current_kernel_time();
+ struct timespec64 now = current_kernel_time64();
if (unlikely(!inode->i_sb)) {
WARN(1, "current_time() called with uninitialized super_block in the inode");
return now;
}
- return timespec_trunc(now, inode->i_sb->s_time_gran);
+ return timespec64_trunc(now, inode->i_sb->s_time_gran);
}
EXPORT_SYMBOL(current_time);
if (ret)
goto fail;
- dir_i->i_mtime = dir_i->i_ctime = ITIME(je32_to_cpu(ri->ctime));
+ dir_i->i_mtime = dir_i->i_ctime = timespec_to_timespec64(ITIME(je32_to_cpu(ri->ctime)));
jffs2_free_raw_inode(ri);
if (dead_f->inocache)
set_nlink(d_inode(dentry), dead_f->inocache->pino_nlink);
if (!ret)
- dir_i->i_mtime = dir_i->i_ctime = ITIME(now);
+ dir_i->i_mtime = dir_i->i_ctime = timespec_to_timespec64(ITIME(now));
return ret;
}
/***********************************************************************/
set_nlink(d_inode(old_dentry), ++f->inocache->pino_nlink);
mutex_unlock(&f->sem);
d_instantiate(dentry, d_inode(old_dentry));
- dir_i->i_mtime = dir_i->i_ctime = ITIME(now);
+ dir_i->i_mtime = dir_i->i_ctime = timespec_to_timespec64(ITIME(now));
ihold(d_inode(old_dentry));
}
return ret;
goto fail;
}
- dir_i->i_mtime = dir_i->i_ctime = ITIME(je32_to_cpu(rd->mctime));
+ dir_i->i_mtime = dir_i->i_ctime = timespec_to_timespec64(ITIME(je32_to_cpu(rd->mctime)));
jffs2_free_raw_dirent(rd);
goto fail;
}
- dir_i->i_mtime = dir_i->i_ctime = ITIME(je32_to_cpu(rd->mctime));
+ dir_i->i_mtime = dir_i->i_ctime = timespec_to_timespec64(ITIME(je32_to_cpu(rd->mctime)));
inc_nlink(dir_i);
jffs2_free_raw_dirent(rd);
ret = jffs2_do_unlink(c, dir_f, dentry->d_name.name,
dentry->d_name.len, f, now);
if (!ret) {
- dir_i->i_mtime = dir_i->i_ctime = ITIME(now);
+ dir_i->i_mtime = dir_i->i_ctime = timespec_to_timespec64(ITIME(now));
clear_nlink(d_inode(dentry));
drop_nlink(dir_i);
}
goto fail;
}
- dir_i->i_mtime = dir_i->i_ctime = ITIME(je32_to_cpu(rd->mctime));
+ dir_i->i_mtime = dir_i->i_ctime = timespec_to_timespec64(ITIME(je32_to_cpu(rd->mctime)));
jffs2_free_raw_dirent(rd);
* caller won't do it on its own since we are returning an error.
*/
d_invalidate(new_dentry);
- new_dir_i->i_mtime = new_dir_i->i_ctime = ITIME(now);
+ new_dir_i->i_mtime = new_dir_i->i_ctime = timespec_to_timespec64(ITIME(now));
return ret;
}
if (d_is_dir(old_dentry))
drop_nlink(old_dir_i);
- new_dir_i->i_mtime = new_dir_i->i_ctime = old_dir_i->i_mtime = old_dir_i->i_ctime = ITIME(now);
+ new_dir_i->i_mtime = new_dir_i->i_ctime = old_dir_i->i_mtime = old_dir_i->i_ctime = timespec_to_timespec64(ITIME(now));
return 0;
}
inode->i_size = pos + writtenlen;
inode->i_blocks = (inode->i_size + 511) >> 9;
- inode->i_ctime = inode->i_mtime = ITIME(je32_to_cpu(ri->ctime));
+ inode->i_ctime = inode->i_mtime = timespec_to_timespec64(ITIME(je32_to_cpu(ri->ctime)));
}
}
return PTR_ERR(new_metadata);
}
/* It worked. Update the inode */
- inode->i_atime = ITIME(je32_to_cpu(ri->atime));
- inode->i_ctime = ITIME(je32_to_cpu(ri->ctime));
- inode->i_mtime = ITIME(je32_to_cpu(ri->mtime));
+ inode->i_atime = timespec_to_timespec64(ITIME(je32_to_cpu(ri->atime)));
+ inode->i_ctime = timespec_to_timespec64(ITIME(je32_to_cpu(ri->ctime)));
+ inode->i_mtime = timespec_to_timespec64(ITIME(je32_to_cpu(ri->mtime)));
inode->i_mode = jemode_to_cpu(ri->mode);
i_uid_write(inode, je16_to_cpu(ri->uid));
i_gid_write(inode, je16_to_cpu(ri->gid));
i_uid_write(inode, je16_to_cpu(latest_node.uid));
i_gid_write(inode, je16_to_cpu(latest_node.gid));
inode->i_size = je32_to_cpu(latest_node.isize);
- inode->i_atime = ITIME(je32_to_cpu(latest_node.atime));
- inode->i_mtime = ITIME(je32_to_cpu(latest_node.mtime));
- inode->i_ctime = ITIME(je32_to_cpu(latest_node.ctime));
+ inode->i_atime = timespec_to_timespec64(ITIME(je32_to_cpu(latest_node.atime)));
+ inode->i_mtime = timespec_to_timespec64(ITIME(je32_to_cpu(latest_node.mtime)));
+ inode->i_ctime = timespec_to_timespec64(ITIME(je32_to_cpu(latest_node.ctime)));
set_nlink(inode, f->inocache->pino_nlink);
if (size > PSIZE) {
/*
* To keep the rest of the code simple. Allocate a
- * contiguous buffer to work with
+ * contiguous buffer to work with. Make the buffer large
+ * enough to make use of the whole extent.
*/
- ea_buf->xattr = kmalloc(size, GFP_KERNEL);
+ ea_buf->max_size = (size + sb->s_blocksize - 1) &
+ ~(sb->s_blocksize - 1);
+
+ ea_buf->xattr = kmalloc(ea_buf->max_size, GFP_KERNEL);
if (ea_buf->xattr == NULL)
return -ENOMEM;
ea_buf->flag = EA_MALLOC;
- ea_buf->max_size = (size + sb->s_blocksize - 1) &
- ~(sb->s_blocksize - 1);
if (ea_size == 0)
return 0;
ps_iattr = parent->iattr;
if (ps_iattr) {
struct iattr *ps_iattrs = &ps_iattr->ia_iattr;
- ktime_get_real_ts(&ps_iattrs->ia_ctime);
+ ktime_get_real_ts64(&ps_iattrs->ia_ctime);
ps_iattrs->ia_mtime = ps_iattrs->ia_ctime;
}
/* update timestamps on the parent */
if (ps_iattr) {
- ktime_get_real_ts(&ps_iattr->ia_iattr.ia_ctime);
+ ktime_get_real_ts64(&ps_iattr->ia_iattr.ia_ctime);
ps_iattr->ia_iattr.ia_mtime =
ps_iattr->ia_iattr.ia_ctime;
}
iattrs->ia_uid = GLOBAL_ROOT_UID;
iattrs->ia_gid = GLOBAL_ROOT_GID;
- ktime_get_real_ts(&iattrs->ia_atime);
+ ktime_get_real_ts64(&iattrs->ia_atime);
iattrs->ia_mtime = iattrs->ia_atime;
iattrs->ia_ctime = iattrs->ia_atime;
struct super_block *sb = inode->i_sb;
inode->i_uid = iattr->ia_uid;
inode->i_gid = iattr->ia_gid;
- inode->i_atime = timespec_trunc(iattr->ia_atime, sb->s_time_gran);
- inode->i_mtime = timespec_trunc(iattr->ia_mtime, sb->s_time_gran);
- inode->i_ctime = timespec_trunc(iattr->ia_ctime, sb->s_time_gran);
+ inode->i_atime = timespec64_trunc(iattr->ia_atime, sb->s_time_gran);
+ inode->i_mtime = timespec64_trunc(iattr->ia_mtime, sb->s_time_gran);
+ inode->i_ctime = timespec64_trunc(iattr->ia_ctime, sb->s_time_gran);
}
static void kernfs_refresh_inode(struct kernfs_node *kn, struct inode *inode)
* exclusive leases. The justification is that if someone has an
* exclusive lease, then they could be modifying it.
*/
-void lease_get_mtime(struct inode *inode, struct timespec *time)
+void lease_get_mtime(struct inode *inode, struct timespec64 *time)
{
bool has_lease = false;
struct file_lock_context *ctx;
return inode_permission(base->d_inode, MAY_EXEC);
}
+/**
+ * try_lookup_one_len - filesystem helper to lookup single pathname component
+ * @name: pathname component to lookup
+ * @base: base directory to lookup from
+ * @len: maximum length @len should be interpreted to
+ *
+ * Look up a dentry by name in the dcache, returning NULL if it does not
+ * currently exist. The function does not try to create a dentry.
+ *
+ * Note that this routine is purely a helper for filesystem usage and should
+ * not be called by generic code.
+ *
+ * The caller must hold base->i_mutex.
+ */
+struct dentry *try_lookup_one_len(const char *name, struct dentry *base, int len)
+{
+ struct qstr this;
+ int err;
+
+ WARN_ON_ONCE(!inode_is_locked(base->d_inode));
+
+ err = lookup_one_len_common(name, base, len, &this);
+ if (err)
+ return ERR_PTR(err);
+
+ return lookup_dcache(&this, base, 0);
+}
+EXPORT_SYMBOL(try_lookup_one_len);
+
/**
* lookup_one_len - filesystem helper to lookup single pathname component
* @name: pathname component to lookup
res->change_attr = delegation->change_attr;
if (nfs_have_writebacks(inode))
res->change_attr++;
- res->ctime = inode->i_ctime;
- res->mtime = inode->i_mtime;
+ res->ctime = timespec64_to_timespec(inode->i_ctime);
+ res->mtime = timespec64_to_timespec(inode->i_mtime);
res->bitmap[0] = (FATTR4_WORD0_CHANGE|FATTR4_WORD0_SIZE) &
args->bitmap[0];
res->bitmap[1] = (FATTR4_WORD1_TIME_METADATA|FATTR4_WORD1_TIME_MODIFY) &
return FSCACHE_CHECKAUX_OBSOLETE;
memset(&auxdata, 0, sizeof(auxdata));
- auxdata.mtime = nfsi->vfs_inode.i_mtime;
- auxdata.ctime = nfsi->vfs_inode.i_ctime;
+ auxdata.mtime = timespec64_to_timespec(nfsi->vfs_inode.i_mtime);
+ auxdata.ctime = timespec64_to_timespec(nfsi->vfs_inode.i_ctime);
if (NFS_SERVER(&nfsi->vfs_inode)->nfs_client->rpc_ops->version == 4)
auxdata.change_attr = inode_peek_iversion_raw(&nfsi->vfs_inode);
return;
memset(&auxdata, 0, sizeof(auxdata));
- auxdata.mtime = nfsi->vfs_inode.i_mtime;
- auxdata.ctime = nfsi->vfs_inode.i_ctime;
+ auxdata.mtime = timespec64_to_timespec(nfsi->vfs_inode.i_mtime);
+ auxdata.ctime = timespec64_to_timespec(nfsi->vfs_inode.i_ctime);
if (NFS_SERVER(&nfsi->vfs_inode)->nfs_client->rpc_ops->version == 4)
auxdata.change_attr = inode_peek_iversion_raw(&nfsi->vfs_inode);
dfprintk(FSCACHE, "NFS: clear cookie (0x%p/0x%p)\n", nfsi, cookie);
memset(&auxdata, 0, sizeof(auxdata));
- auxdata.mtime = nfsi->vfs_inode.i_mtime;
- auxdata.ctime = nfsi->vfs_inode.i_ctime;
+ auxdata.mtime = timespec64_to_timespec(nfsi->vfs_inode.i_mtime);
+ auxdata.ctime = timespec64_to_timespec(nfsi->vfs_inode.i_ctime);
fscache_relinquish_cookie(cookie, &auxdata, false);
nfsi->fscache = NULL;
}
return;
memset(&auxdata, 0, sizeof(auxdata));
- auxdata.mtime = nfsi->vfs_inode.i_mtime;
- auxdata.ctime = nfsi->vfs_inode.i_ctime;
+ auxdata.mtime = timespec64_to_timespec(nfsi->vfs_inode.i_mtime);
+ auxdata.ctime = timespec64_to_timespec(nfsi->vfs_inode.i_ctime);
if (inode_is_open_for_write(inode)) {
dfprintk(FSCACHE, "NFS: nfsi 0x%p disabling cache\n", nfsi);
nfsi->read_cache_jiffies = fattr->time_start;
nfsi->attr_gencount = fattr->gencount;
if (fattr->valid & NFS_ATTR_FATTR_ATIME)
- inode->i_atime = fattr->atime;
+ inode->i_atime = timespec_to_timespec64(fattr->atime);
else if (nfs_server_capable(inode, NFS_CAP_ATIME))
nfs_set_cache_invalid(inode, NFS_INO_INVALID_ATIME);
if (fattr->valid & NFS_ATTR_FATTR_MTIME)
- inode->i_mtime = fattr->mtime;
+ inode->i_mtime = timespec_to_timespec64(fattr->mtime);
else if (nfs_server_capable(inode, NFS_CAP_MTIME))
nfs_set_cache_invalid(inode, NFS_INO_INVALID_MTIME);
if (fattr->valid & NFS_ATTR_FATTR_CTIME)
- inode->i_ctime = fattr->ctime;
+ inode->i_ctime = timespec_to_timespec64(fattr->ctime);
else if (nfs_server_capable(inode, NFS_CAP_CTIME))
nfs_set_cache_invalid(inode, NFS_INO_INVALID_CTIME);
if (fattr->valid & NFS_ATTR_FATTR_CHANGE)
if ((attr->ia_valid & ATTR_GID) != 0)
inode->i_gid = attr->ia_gid;
if (fattr->valid & NFS_ATTR_FATTR_CTIME)
- inode->i_ctime = fattr->ctime;
+ inode->i_ctime = timespec_to_timespec64(fattr->ctime);
else
nfs_set_cache_invalid(inode, NFS_INO_INVALID_CHANGE
| NFS_INO_INVALID_CTIME);
NFS_I(inode)->cache_validity &= ~(NFS_INO_INVALID_ATIME
| NFS_INO_INVALID_CTIME);
if (fattr->valid & NFS_ATTR_FATTR_ATIME)
- inode->i_atime = fattr->atime;
+ inode->i_atime = timespec_to_timespec64(fattr->atime);
else if (attr->ia_valid & ATTR_ATIME_SET)
inode->i_atime = attr->ia_atime;
else
nfs_set_cache_invalid(inode, NFS_INO_INVALID_ATIME);
if (fattr->valid & NFS_ATTR_FATTR_CTIME)
- inode->i_ctime = fattr->ctime;
+ inode->i_ctime = timespec_to_timespec64(fattr->ctime);
else
nfs_set_cache_invalid(inode, NFS_INO_INVALID_CHANGE
| NFS_INO_INVALID_CTIME);
NFS_I(inode)->cache_validity &= ~(NFS_INO_INVALID_MTIME
| NFS_INO_INVALID_CTIME);
if (fattr->valid & NFS_ATTR_FATTR_MTIME)
- inode->i_mtime = fattr->mtime;
+ inode->i_mtime = timespec_to_timespec64(fattr->mtime);
else if (attr->ia_valid & ATTR_MTIME_SET)
inode->i_mtime = attr->ia_mtime;
else
nfs_set_cache_invalid(inode, NFS_INO_INVALID_MTIME);
if (fattr->valid & NFS_ATTR_FATTR_CTIME)
- inode->i_ctime = fattr->ctime;
+ inode->i_ctime = timespec_to_timespec64(fattr->ctime);
else
nfs_set_cache_invalid(inode, NFS_INO_INVALID_CHANGE
| NFS_INO_INVALID_CTIME);
static void nfs_wcc_update_inode(struct inode *inode, struct nfs_fattr *fattr)
{
+ struct timespec ts;
+
if ((fattr->valid & NFS_ATTR_FATTR_PRECHANGE)
&& (fattr->valid & NFS_ATTR_FATTR_CHANGE)
&& inode_eq_iversion_raw(inode, fattr->pre_change_attr)) {
nfs_set_cache_invalid(inode, NFS_INO_INVALID_DATA);
}
/* If we have atomic WCC data, we may update some attributes */
+ ts = timespec64_to_timespec(inode->i_ctime);
if ((fattr->valid & NFS_ATTR_FATTR_PRECTIME)
&& (fattr->valid & NFS_ATTR_FATTR_CTIME)
- && timespec_equal(&inode->i_ctime, &fattr->pre_ctime)) {
- memcpy(&inode->i_ctime, &fattr->ctime, sizeof(inode->i_ctime));
+ && timespec_equal(&ts, &fattr->pre_ctime)) {
+ inode->i_ctime = timespec_to_timespec64(fattr->ctime);
}
+ ts = timespec64_to_timespec(inode->i_mtime);
if ((fattr->valid & NFS_ATTR_FATTR_PREMTIME)
&& (fattr->valid & NFS_ATTR_FATTR_MTIME)
- && timespec_equal(&inode->i_mtime, &fattr->pre_mtime)) {
- memcpy(&inode->i_mtime, &fattr->mtime, sizeof(inode->i_mtime));
+ && timespec_equal(&ts, &fattr->pre_mtime)) {
+ inode->i_mtime = timespec_to_timespec64(fattr->mtime);
if (S_ISDIR(inode->i_mode))
nfs_set_cache_invalid(inode, NFS_INO_INVALID_DATA);
}
struct nfs_inode *nfsi = NFS_I(inode);
loff_t cur_size, new_isize;
unsigned long invalid = 0;
-
+ struct timespec ts;
if (NFS_PROTO(inode)->have_delegation(inode, FMODE_READ))
return 0;
invalid |= NFS_INO_INVALID_CHANGE
| NFS_INO_REVAL_PAGECACHE;
- if ((fattr->valid & NFS_ATTR_FATTR_MTIME) && !timespec_equal(&inode->i_mtime, &fattr->mtime))
+ ts = timespec64_to_timespec(inode->i_mtime);
+ if ((fattr->valid & NFS_ATTR_FATTR_MTIME) && !timespec_equal(&ts, &fattr->mtime))
invalid |= NFS_INO_INVALID_MTIME;
- if ((fattr->valid & NFS_ATTR_FATTR_CTIME) && !timespec_equal(&inode->i_ctime, &fattr->ctime))
+ ts = timespec64_to_timespec(inode->i_ctime);
+ if ((fattr->valid & NFS_ATTR_FATTR_CTIME) && !timespec_equal(&ts, &fattr->ctime))
invalid |= NFS_INO_INVALID_CTIME;
if (fattr->valid & NFS_ATTR_FATTR_SIZE) {
if ((fattr->valid & NFS_ATTR_FATTR_NLINK) && inode->i_nlink != fattr->nlink)
invalid |= NFS_INO_INVALID_OTHER;
- if ((fattr->valid & NFS_ATTR_FATTR_ATIME) && !timespec_equal(&inode->i_atime, &fattr->atime))
+ ts = timespec64_to_timespec(inode->i_atime);
+ if ((fattr->valid & NFS_ATTR_FATTR_ATIME) && !timespec_equal(&ts, &fattr->atime))
invalid |= NFS_INO_INVALID_ATIME;
if (invalid != 0)
}
if ((fattr->valid & NFS_ATTR_FATTR_CTIME) != 0 &&
(fattr->valid & NFS_ATTR_FATTR_PRECTIME) == 0) {
- memcpy(&fattr->pre_ctime, &inode->i_ctime, sizeof(fattr->pre_ctime));
+ fattr->pre_ctime = timespec64_to_timespec(inode->i_ctime);
fattr->valid |= NFS_ATTR_FATTR_PRECTIME;
}
if ((fattr->valid & NFS_ATTR_FATTR_MTIME) != 0 &&
(fattr->valid & NFS_ATTR_FATTR_PREMTIME) == 0) {
- memcpy(&fattr->pre_mtime, &inode->i_mtime, sizeof(fattr->pre_mtime));
+ fattr->pre_mtime = timespec64_to_timespec(inode->i_mtime);
fattr->valid |= NFS_ATTR_FATTR_PREMTIME;
}
if ((fattr->valid & NFS_ATTR_FATTR_SIZE) != 0 &&
}
if (fattr->valid & NFS_ATTR_FATTR_MTIME) {
- memcpy(&inode->i_mtime, &fattr->mtime, sizeof(inode->i_mtime));
+ inode->i_mtime = timespec_to_timespec64(fattr->mtime);
} else if (server->caps & NFS_CAP_MTIME) {
nfsi->cache_validity |= save_cache_validity &
(NFS_INO_INVALID_MTIME
}
if (fattr->valid & NFS_ATTR_FATTR_CTIME) {
- memcpy(&inode->i_ctime, &fattr->ctime, sizeof(inode->i_ctime));
+ inode->i_ctime = timespec_to_timespec64(fattr->ctime);
} else if (server->caps & NFS_CAP_CTIME) {
nfsi->cache_validity |= save_cache_validity &
(NFS_INO_INVALID_CTIME
if (fattr->valid & NFS_ATTR_FATTR_ATIME)
- memcpy(&inode->i_atime, &fattr->atime, sizeof(inode->i_atime));
+ inode->i_atime = timespec_to_timespec64(fattr->atime);
else if (server->caps & NFS_CAP_ATIME) {
nfsi->cache_validity |= save_cache_validity &
(NFS_INO_INVALID_ATIME
static void encode_sattr(struct xdr_stream *xdr, const struct iattr *attr)
{
+ struct timespec ts;
__be32 *p;
p = xdr_reserve_space(xdr, NFS_sattr_sz << 2);
else
*p++ = cpu_to_be32(NFS2_SATTR_NOT_SET);
- if (attr->ia_valid & ATTR_ATIME_SET)
- p = xdr_encode_time(p, &attr->ia_atime);
- else if (attr->ia_valid & ATTR_ATIME)
- p = xdr_encode_current_server_time(p, &attr->ia_atime);
- else
+ if (attr->ia_valid & ATTR_ATIME_SET) {
+ ts = timespec64_to_timespec(attr->ia_atime);
+ p = xdr_encode_time(p, &ts);
+ } else if (attr->ia_valid & ATTR_ATIME) {
+ ts = timespec64_to_timespec(attr->ia_atime);
+ p = xdr_encode_current_server_time(p, &ts);
+ } else
p = xdr_time_not_set(p);
- if (attr->ia_valid & ATTR_MTIME_SET)
- xdr_encode_time(p, &attr->ia_mtime);
- else if (attr->ia_valid & ATTR_MTIME)
- xdr_encode_current_server_time(p, &attr->ia_mtime);
- else
+ if (attr->ia_valid & ATTR_MTIME_SET) {
+ ts = timespec64_to_timespec(attr->ia_atime);
+ xdr_encode_time(p, &ts);
+ } else if (attr->ia_valid & ATTR_MTIME) {
+ ts = timespec64_to_timespec(attr->ia_mtime);
+ xdr_encode_current_server_time(p, &ts);
+ } else
xdr_time_not_set(p);
}
*/
static void encode_sattr3(struct xdr_stream *xdr, const struct iattr *attr)
{
+ struct timespec ts;
u32 nbytes;
__be32 *p;
*p++ = xdr_zero;
if (attr->ia_valid & ATTR_ATIME_SET) {
+ struct timespec ts;
*p++ = xdr_two;
- p = xdr_encode_nfstime3(p, &attr->ia_atime);
+ ts = timespec64_to_timespec(attr->ia_atime);
+ p = xdr_encode_nfstime3(p, &ts);
} else if (attr->ia_valid & ATTR_ATIME) {
*p++ = xdr_one;
} else
if (attr->ia_valid & ATTR_MTIME_SET) {
*p++ = xdr_two;
- xdr_encode_nfstime3(p, &attr->ia_mtime);
+ ts = timespec64_to_timespec(attr->ia_mtime);
+ xdr_encode_nfstime3(p, &ts);
} else if (attr->ia_valid & ATTR_MTIME) {
*p = xdr_one;
} else
const struct nfs_server *server,
const uint32_t attrmask[])
{
+ struct timespec ts;
char owner_name[IDMAP_NAMESZ];
char owner_group[IDMAP_NAMESZ];
int owner_namelen = 0;
if (bmval[1] & FATTR4_WORD1_TIME_ACCESS_SET) {
if (iap->ia_valid & ATTR_ATIME_SET) {
*p++ = cpu_to_be32(NFS4_SET_TO_CLIENT_TIME);
- p = xdr_encode_nfstime4(p, &iap->ia_atime);
+ ts = timespec64_to_timespec(iap->ia_atime);
+ p = xdr_encode_nfstime4(p, &ts);
} else
*p++ = cpu_to_be32(NFS4_SET_TO_SERVER_TIME);
}
if (bmval[1] & FATTR4_WORD1_TIME_MODIFY_SET) {
if (iap->ia_valid & ATTR_MTIME_SET) {
*p++ = cpu_to_be32(NFS4_SET_TO_CLIENT_TIME);
- p = xdr_encode_nfstime4(p, &iap->ia_mtime);
+ ts = timespec64_to_timespec(iap->ia_mtime);
+ p = xdr_encode_nfstime4(p, &ts);
} else
*p++ = cpu_to_be32(NFS4_SET_TO_SERVER_TIME);
}
{
loff_t new_size = lcp->lc_last_wr + 1;
struct iattr iattr = { .ia_valid = 0 };
+ struct timespec ts;
int error;
+ ts = timespec64_to_timespec(inode->i_mtime);
if (lcp->lc_mtime.tv_nsec == UTIME_NOW ||
- timespec_compare(&lcp->lc_mtime, &inode->i_mtime) < 0)
- lcp->lc_mtime = current_time(inode);
+ timespec_compare(&lcp->lc_mtime, &ts) < 0)
+ lcp->lc_mtime = timespec64_to_timespec(current_time(inode));
iattr.ia_valid |= ATTR_ATIME | ATTR_CTIME | ATTR_MTIME;
- iattr.ia_atime = iattr.ia_ctime = iattr.ia_mtime = lcp->lc_mtime;
+ iattr.ia_atime = iattr.ia_ctime = iattr.ia_mtime = timespec_to_timespec64(lcp->lc_mtime);
if (new_size > i_size_read(inode)) {
iattr.ia_valid |= ATTR_SIZE;
encode_fattr3(struct svc_rqst *rqstp, __be32 *p, struct svc_fh *fhp,
struct kstat *stat)
{
+ struct timespec ts;
*p++ = htonl(nfs3_ftypes[(stat->mode & S_IFMT) >> 12]);
*p++ = htonl((u32) (stat->mode & S_IALLUGO));
*p++ = htonl((u32) stat->nlink);
*p++ = htonl((u32) MINOR(stat->rdev));
p = encode_fsid(p, fhp);
p = xdr_encode_hyper(p, stat->ino);
- p = encode_time3(p, &stat->atime);
- p = encode_time3(p, &stat->mtime);
- p = encode_time3(p, &stat->ctime);
+ ts = timespec64_to_timespec(stat->atime);
+ p = encode_time3(p, &ts);
+ ts = timespec64_to_timespec(stat->mtime);
+ p = encode_time3(p, &ts);
+ ts = timespec64_to_timespec(stat->ctime);
+ p = encode_time3(p, &ts);
return p;
}
stat.size = inode->i_size;
}
- fhp->fh_pre_mtime = stat.mtime;
- fhp->fh_pre_ctime = stat.ctime;
+ fhp->fh_pre_mtime = timespec64_to_timespec(stat.mtime);
+ fhp->fh_pre_ctime = timespec64_to_timespec(stat.ctime);
fhp->fh_pre_size = stat.size;
fhp->fh_pre_change = nfsd4_change_attribute(&stat, inode);
fhp->fh_pre_saved = true;
struct iattr *iattr, struct nfs4_acl **acl,
struct xdr_netobj *label, int *umask)
{
+ struct timespec ts;
int expected_len, len = 0;
u32 dummy32;
char *buf;
switch (dummy32) {
case NFS4_SET_TO_CLIENT_TIME:
len += 12;
- status = nfsd4_decode_time(argp, &iattr->ia_atime);
+ status = nfsd4_decode_time(argp, &ts);
+ iattr->ia_atime = timespec_to_timespec64(ts);
if (status)
return status;
iattr->ia_valid |= (ATTR_ATIME | ATTR_ATIME_SET);
switch (dummy32) {
case NFS4_SET_TO_CLIENT_TIME:
len += 12;
- status = nfsd4_decode_time(argp, &iattr->ia_mtime);
+ status = nfsd4_decode_time(argp, &ts);
+ iattr->ia_mtime = timespec_to_timespec64(ts);
if (status)
return status;
iattr->ia_valid |= (ATTR_MTIME | ATTR_MTIME_SET);
{
struct dentry *dentry = fhp->fh_dentry;
int type;
- struct timespec time;
+ struct timespec64 time;
u32 f;
type = (stat->mode & S_IFMT);
*/
static int dnotify_handle_event(struct fsnotify_group *group,
struct inode *inode,
- struct fsnotify_mark *inode_mark,
- struct fsnotify_mark *vfsmount_mark,
u32 mask, const void *data, int data_type,
const unsigned char *file_name, u32 cookie,
struct fsnotify_iter_info *iter_info)
{
+ struct fsnotify_mark *inode_mark = fsnotify_iter_inode_mark(iter_info);
struct dnotify_mark *dn_mark;
struct dnotify_struct *dn;
struct dnotify_struct **prev;
if (!S_ISDIR(inode->i_mode))
return 0;
- BUG_ON(vfsmount_mark);
+ if (WARN_ON(fsnotify_iter_vfsmount_mark(iter_info)))
+ return 0;
dn_mark = container_of(inode_mark, struct dnotify_mark, fsn_mark);
dn_mark = container_of(fsn_mark, struct dnotify_mark, fsn_mark);
spin_lock(&fsn_mark->lock);
} else {
- error = fsnotify_add_mark_locked(new_fsn_mark, inode, NULL, 0);
+ error = fsnotify_add_inode_mark_locked(new_fsn_mark, inode, 0);
if (error) {
mutex_unlock(&dnotify_group->mark_mutex);
goto out_err;
return ret;
}
-static bool fanotify_should_send_event(struct fsnotify_mark *inode_mark,
- struct fsnotify_mark *vfsmnt_mark,
- u32 event_mask,
- const void *data, int data_type)
+static bool fanotify_should_send_event(struct fsnotify_iter_info *iter_info,
+ u32 event_mask, const void *data,
+ int data_type)
{
__u32 marks_mask = 0, marks_ignored_mask = 0;
const struct path *path = data;
+ struct fsnotify_mark *mark;
+ int type;
- pr_debug("%s: inode_mark=%p vfsmnt_mark=%p mask=%x data=%p"
- " data_type=%d\n", __func__, inode_mark, vfsmnt_mark,
- event_mask, data, data_type);
+ pr_debug("%s: report_mask=%x mask=%x data=%p data_type=%d\n",
+ __func__, iter_info->report_mask, event_mask, data, data_type);
/* if we don't have enough info to send an event to userspace say no */
if (data_type != FSNOTIFY_EVENT_PATH)
!d_can_lookup(path->dentry))
return false;
- /*
- * if the event is for a child and this inode doesn't care about
- * events on the child, don't send it!
- */
- if (inode_mark &&
- (!(event_mask & FS_EVENT_ON_CHILD) ||
- (inode_mark->mask & FS_EVENT_ON_CHILD))) {
- marks_mask |= inode_mark->mask;
- marks_ignored_mask |= inode_mark->ignored_mask;
- }
+ fsnotify_foreach_obj_type(type) {
+ if (!fsnotify_iter_should_report_type(iter_info, type))
+ continue;
+ mark = iter_info->marks[type];
+ /*
+ * if the event is for a child and this inode doesn't care about
+ * events on the child, don't send it!
+ */
+ if (type == FSNOTIFY_OBJ_TYPE_INODE &&
+ (event_mask & FS_EVENT_ON_CHILD) &&
+ !(mark->mask & FS_EVENT_ON_CHILD))
+ continue;
- if (vfsmnt_mark) {
- marks_mask |= vfsmnt_mark->mask;
- marks_ignored_mask |= vfsmnt_mark->ignored_mask;
+ marks_mask |= mark->mask;
+ marks_ignored_mask |= mark->ignored_mask;
}
if (d_is_dir(path->dentry) &&
static int fanotify_handle_event(struct fsnotify_group *group,
struct inode *inode,
- struct fsnotify_mark *inode_mark,
- struct fsnotify_mark *fanotify_mark,
u32 mask, const void *data, int data_type,
const unsigned char *file_name, u32 cookie,
struct fsnotify_iter_info *iter_info)
BUILD_BUG_ON(FAN_ACCESS_PERM != FS_ACCESS_PERM);
BUILD_BUG_ON(FAN_ONDIR != FS_ISDIR);
- if (!fanotify_should_send_event(inode_mark, fanotify_mark, mask, data,
- data_type))
+ if (!fanotify_should_send_event(iter_info, mask, data, data_type))
return 0;
pr_debug("%s: group=%p inode=%p mask=%x\n", __func__, group, inode,
struct inotify_inode_mark *inode_mark;
struct inode *inode;
- if (!(mark->connector->flags & FSNOTIFY_OBJ_TYPE_INODE))
+ if (mark->connector->type != FSNOTIFY_OBJ_TYPE_INODE)
return;
inode_mark = container_of(mark, struct inotify_inode_mark, fsn_mark);
if (mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY)
mflags |= FAN_MARK_IGNORED_SURV_MODIFY;
- if (mark->connector->flags & FSNOTIFY_OBJ_TYPE_INODE) {
+ if (mark->connector->type == FSNOTIFY_OBJ_TYPE_INODE) {
inode = igrab(mark->connector->inode);
if (!inode)
return;
show_mark_fhandle(m, inode);
seq_putc(m, '\n');
iput(inode);
- } else if (mark->connector->flags & FSNOTIFY_OBJ_TYPE_VFSMOUNT) {
+ } else if (mark->connector->type == FSNOTIFY_OBJ_TYPE_VFSMOUNT) {
struct mount *mnt = real_mount(mark->connector->mnt);
seq_printf(m, "fanotify mnt_id:%x mflags:%x mask:%x ignored_mask:%x\n",
EXPORT_SYMBOL_GPL(__fsnotify_parent);
static int send_to_group(struct inode *to_tell,
- struct fsnotify_mark *inode_mark,
- struct fsnotify_mark *vfsmount_mark,
__u32 mask, const void *data,
int data_is, u32 cookie,
const unsigned char *file_name,
__u32 test_mask = (mask & ~FS_EVENT_ON_CHILD);
__u32 marks_mask = 0;
__u32 marks_ignored_mask = 0;
+ struct fsnotify_mark *mark;
+ int type;
- if (unlikely(!inode_mark && !vfsmount_mark)) {
- BUG();
+ if (WARN_ON(!iter_info->report_mask))
return 0;
- }
/* clear ignored on inode modification */
if (mask & FS_MODIFY) {
- if (inode_mark &&
- !(inode_mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY))
- inode_mark->ignored_mask = 0;
- if (vfsmount_mark &&
- !(vfsmount_mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY))
- vfsmount_mark->ignored_mask = 0;
- }
-
- /* does the inode mark tell us to do something? */
- if (inode_mark) {
- group = inode_mark->group;
- marks_mask |= inode_mark->mask;
- marks_ignored_mask |= inode_mark->ignored_mask;
+ fsnotify_foreach_obj_type(type) {
+ if (!fsnotify_iter_should_report_type(iter_info, type))
+ continue;
+ mark = iter_info->marks[type];
+ if (mark &&
+ !(mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY))
+ mark->ignored_mask = 0;
+ }
}
- /* does the vfsmount_mark tell us to do something? */
- if (vfsmount_mark) {
- group = vfsmount_mark->group;
- marks_mask |= vfsmount_mark->mask;
- marks_ignored_mask |= vfsmount_mark->ignored_mask;
+ fsnotify_foreach_obj_type(type) {
+ if (!fsnotify_iter_should_report_type(iter_info, type))
+ continue;
+ mark = iter_info->marks[type];
+ /* does the object mark tell us to do something? */
+ if (mark) {
+ group = mark->group;
+ marks_mask |= mark->mask;
+ marks_ignored_mask |= mark->ignored_mask;
+ }
}
- pr_debug("%s: group=%p to_tell=%p mask=%x inode_mark=%p"
- " vfsmount_mark=%p marks_mask=%x marks_ignored_mask=%x"
+ pr_debug("%s: group=%p to_tell=%p mask=%x marks_mask=%x marks_ignored_mask=%x"
" data=%p data_is=%d cookie=%d\n",
- __func__, group, to_tell, mask, inode_mark, vfsmount_mark,
- marks_mask, marks_ignored_mask, data,
- data_is, cookie);
+ __func__, group, to_tell, mask, marks_mask, marks_ignored_mask,
+ data, data_is, cookie);
if (!(test_mask & marks_mask & ~marks_ignored_mask))
return 0;
- return group->ops->handle_event(group, to_tell, inode_mark,
- vfsmount_mark, mask, data, data_is,
+ return group->ops->handle_event(group, to_tell, mask, data, data_is,
file_name, cookie, iter_info);
}
return hlist_entry_safe(node, struct fsnotify_mark, obj_list);
}
+/*
+ * iter_info is a multi head priority queue of marks.
+ * Pick a subset of marks from queue heads, all with the
+ * same group and set the report_mask for selected subset.
+ * Returns the report_mask of the selected subset.
+ */
+static unsigned int fsnotify_iter_select_report_types(
+ struct fsnotify_iter_info *iter_info)
+{
+ struct fsnotify_group *max_prio_group = NULL;
+ struct fsnotify_mark *mark;
+ int type;
+
+ /* Choose max prio group among groups of all queue heads */
+ fsnotify_foreach_obj_type(type) {
+ mark = iter_info->marks[type];
+ if (mark &&
+ fsnotify_compare_groups(max_prio_group, mark->group) > 0)
+ max_prio_group = mark->group;
+ }
+
+ if (!max_prio_group)
+ return 0;
+
+ /* Set the report mask for marks from same group as max prio group */
+ iter_info->report_mask = 0;
+ fsnotify_foreach_obj_type(type) {
+ mark = iter_info->marks[type];
+ if (mark &&
+ fsnotify_compare_groups(max_prio_group, mark->group) == 0)
+ fsnotify_iter_set_report_type(iter_info, type);
+ }
+
+ return iter_info->report_mask;
+}
+
+/*
+ * Pop from iter_info multi head queue, the marks that were iterated in the
+ * current iteration step.
+ */
+static void fsnotify_iter_next(struct fsnotify_iter_info *iter_info)
+{
+ int type;
+
+ fsnotify_foreach_obj_type(type) {
+ if (fsnotify_iter_should_report_type(iter_info, type))
+ iter_info->marks[type] =
+ fsnotify_next_mark(iter_info->marks[type]);
+ }
+}
+
/*
* This is the main call to fsnotify. The VFS calls into hook specific functions
* in linux/fsnotify.h. Those functions then in turn call here. Here will call
if ((mask & FS_MODIFY) ||
(test_mask & to_tell->i_fsnotify_mask)) {
- iter_info.inode_mark =
+ iter_info.marks[FSNOTIFY_OBJ_TYPE_INODE] =
fsnotify_first_mark(&to_tell->i_fsnotify_marks);
}
if (mnt && ((mask & FS_MODIFY) ||
(test_mask & mnt->mnt_fsnotify_mask))) {
- iter_info.inode_mark =
+ iter_info.marks[FSNOTIFY_OBJ_TYPE_INODE] =
fsnotify_first_mark(&to_tell->i_fsnotify_marks);
- iter_info.vfsmount_mark =
+ iter_info.marks[FSNOTIFY_OBJ_TYPE_VFSMOUNT] =
fsnotify_first_mark(&mnt->mnt_fsnotify_marks);
}
* ignore masks are properly reflected for mount mark notifications.
* That's why this traversal is so complicated...
*/
- while (iter_info.inode_mark || iter_info.vfsmount_mark) {
- struct fsnotify_mark *inode_mark = iter_info.inode_mark;
- struct fsnotify_mark *vfsmount_mark = iter_info.vfsmount_mark;
-
- if (inode_mark && vfsmount_mark) {
- int cmp = fsnotify_compare_groups(inode_mark->group,
- vfsmount_mark->group);
- if (cmp > 0)
- inode_mark = NULL;
- else if (cmp < 0)
- vfsmount_mark = NULL;
- }
-
- ret = send_to_group(to_tell, inode_mark, vfsmount_mark, mask,
- data, data_is, cookie, file_name,
- &iter_info);
+ while (fsnotify_iter_select_report_types(&iter_info)) {
+ ret = send_to_group(to_tell, mask, data, data_is, cookie,
+ file_name, &iter_info);
if (ret && (mask & ALL_FSNOTIFY_PERM_EVENTS))
goto out;
- if (inode_mark)
- iter_info.inode_mark =
- fsnotify_next_mark(iter_info.inode_mark);
- if (vfsmount_mark)
- iter_info.vfsmount_mark =
- fsnotify_next_mark(iter_info.vfsmount_mark);
+ fsnotify_iter_next(&iter_info);
}
ret = 0;
out:
#include "../mount.h"
-struct fsnotify_iter_info {
- struct fsnotify_mark *inode_mark;
- struct fsnotify_mark *vfsmount_mark;
- int srcu_idx;
-};
-
/* destroy all events sitting in this groups notification queue */
extern void fsnotify_flush_notify(struct fsnotify_group *group);
fsnotify_group_stop_queueing(group);
/* Clear all marks for this group and queue them for destruction */
- fsnotify_clear_marks_by_group(group, FSNOTIFY_OBJ_ALL_TYPES);
+ fsnotify_clear_marks_by_group(group, FSNOTIFY_OBJ_ALL_TYPES_MASK);
/*
* Some marks can still be pinned when waiting for response from
struct fsnotify_group *group);
extern int inotify_handle_event(struct fsnotify_group *group,
struct inode *inode,
- struct fsnotify_mark *inode_mark,
- struct fsnotify_mark *vfsmount_mark,
u32 mask, const void *data, int data_type,
const unsigned char *file_name, u32 cookie,
struct fsnotify_iter_info *iter_info);
int inotify_handle_event(struct fsnotify_group *group,
struct inode *inode,
- struct fsnotify_mark *inode_mark,
- struct fsnotify_mark *vfsmount_mark,
u32 mask, const void *data, int data_type,
const unsigned char *file_name, u32 cookie,
struct fsnotify_iter_info *iter_info)
{
+ struct fsnotify_mark *inode_mark = fsnotify_iter_inode_mark(iter_info);
struct inotify_inode_mark *i_mark;
struct inotify_event_info *event;
struct fsnotify_event *fsn_event;
int len = 0;
int alloc_len = sizeof(struct inotify_event_info);
- BUG_ON(vfsmount_mark);
+ if (WARN_ON(fsnotify_iter_vfsmount_mark(iter_info)))
+ return 0;
if ((inode_mark->mask & FS_EXCL_UNLINK) &&
(data_type == FSNOTIFY_EVENT_PATH)) {
struct fsnotify_group *group)
{
struct inotify_inode_mark *i_mark;
+ struct fsnotify_iter_info iter_info = { };
+
+ fsnotify_iter_set_report_type_mark(&iter_info, FSNOTIFY_OBJ_TYPE_INODE,
+ fsn_mark);
/* Queue ignore event for the watch */
- inotify_handle_event(group, NULL, fsn_mark, NULL, FS_IN_IGNORED,
- NULL, FSNOTIFY_EVENT_NONE, NULL, 0, NULL);
+ inotify_handle_event(group, NULL, FS_IN_IGNORED, NULL,
+ FSNOTIFY_EVENT_NONE, NULL, 0, &iter_info);
i_mark = container_of(fsn_mark, struct inotify_inode_mark, fsn_mark);
/* remove this mark from the idr */
}
/* we are on the idr, now get on the inode */
- ret = fsnotify_add_mark_locked(&tmp_i_mark->fsn_mark, inode, NULL, 0);
+ ret = fsnotify_add_inode_mark_locked(&tmp_i_mark->fsn_mark, inode, 0);
if (ret) {
/* we failed to get on the inode, get off the idr */
inotify_remove_from_idr(group, tmp_i_mark);
if (mark->flags & FSNOTIFY_MARK_FLAG_ATTACHED)
new_mask |= mark->mask;
}
- if (conn->flags & FSNOTIFY_OBJ_TYPE_INODE)
+ if (conn->type == FSNOTIFY_OBJ_TYPE_INODE)
conn->inode->i_fsnotify_mask = new_mask;
- else if (conn->flags & FSNOTIFY_OBJ_TYPE_VFSMOUNT)
+ else if (conn->type == FSNOTIFY_OBJ_TYPE_VFSMOUNT)
real_mount(conn->mnt)->mnt_fsnotify_mask = new_mask;
}
spin_lock(&conn->lock);
__fsnotify_recalc_mask(conn);
spin_unlock(&conn->lock);
- if (conn->flags & FSNOTIFY_OBJ_TYPE_INODE)
+ if (conn->type == FSNOTIFY_OBJ_TYPE_INODE)
__fsnotify_update_child_dentry_flags(conn->inode);
}
{
struct inode *inode = NULL;
- if (conn->flags & FSNOTIFY_OBJ_TYPE_INODE) {
+ if (conn->type == FSNOTIFY_OBJ_TYPE_INODE) {
inode = conn->inode;
rcu_assign_pointer(inode->i_fsnotify_marks, NULL);
inode->i_fsnotify_mask = 0;
conn->inode = NULL;
- conn->flags &= ~FSNOTIFY_OBJ_TYPE_INODE;
- } else if (conn->flags & FSNOTIFY_OBJ_TYPE_VFSMOUNT) {
+ conn->type = FSNOTIFY_OBJ_TYPE_DETACHED;
+ } else if (conn->type == FSNOTIFY_OBJ_TYPE_VFSMOUNT) {
rcu_assign_pointer(real_mount(conn->mnt)->mnt_fsnotify_marks,
NULL);
real_mount(conn->mnt)->mnt_fsnotify_mask = 0;
conn->mnt = NULL;
- conn->flags &= ~FSNOTIFY_OBJ_TYPE_VFSMOUNT;
+ conn->type = FSNOTIFY_OBJ_TYPE_DETACHED;
}
return inode;
bool fsnotify_prepare_user_wait(struct fsnotify_iter_info *iter_info)
{
- /* This can fail if mark is being removed */
- if (!fsnotify_get_mark_safe(iter_info->inode_mark))
- return false;
- if (!fsnotify_get_mark_safe(iter_info->vfsmount_mark)) {
- fsnotify_put_mark_wake(iter_info->inode_mark);
- return false;
+ int type;
+
+ fsnotify_foreach_obj_type(type) {
+ /* This can fail if mark is being removed */
+ if (!fsnotify_get_mark_safe(iter_info->marks[type]))
+ goto fail;
}
/*
srcu_read_unlock(&fsnotify_mark_srcu, iter_info->srcu_idx);
return true;
+
+fail:
+ for (type--; type >= 0; type--)
+ fsnotify_put_mark_wake(iter_info->marks[type]);
+ return false;
}
void fsnotify_finish_user_wait(struct fsnotify_iter_info *iter_info)
{
+ int type;
+
iter_info->srcu_idx = srcu_read_lock(&fsnotify_mark_srcu);
- fsnotify_put_mark_wake(iter_info->inode_mark);
- fsnotify_put_mark_wake(iter_info->vfsmount_mark);
+ fsnotify_foreach_obj_type(type)
+ fsnotify_put_mark_wake(iter_info->marks[type]);
}
/*
spin_lock_init(&conn->lock);
INIT_HLIST_HEAD(&conn->list);
if (inode) {
- conn->flags = FSNOTIFY_OBJ_TYPE_INODE;
+ conn->type = FSNOTIFY_OBJ_TYPE_INODE;
conn->inode = igrab(inode);
} else {
- conn->flags = FSNOTIFY_OBJ_TYPE_VFSMOUNT;
+ conn->type = FSNOTIFY_OBJ_TYPE_VFSMOUNT;
conn->mnt = mnt;
}
/*
if (!conn)
goto out;
spin_lock(&conn->lock);
- if (!(conn->flags & (FSNOTIFY_OBJ_TYPE_INODE |
- FSNOTIFY_OBJ_TYPE_VFSMOUNT))) {
+ if (conn->type == FSNOTIFY_OBJ_TYPE_DETACHED) {
spin_unlock(&conn->lock);
srcu_read_unlock(&fsnotify_mark_srcu, idx);
return NULL;
return NULL;
}
-/* Clear any marks in a group with given type */
+/* Clear any marks in a group with given type mask */
void fsnotify_clear_marks_by_group(struct fsnotify_group *group,
- unsigned int type)
+ unsigned int type_mask)
{
struct fsnotify_mark *lmark, *mark;
LIST_HEAD(to_free);
struct list_head *head = &to_free;
/* Skip selection step if we want to clear all marks. */
- if (type == FSNOTIFY_OBJ_ALL_TYPES) {
+ if (type_mask == FSNOTIFY_OBJ_ALL_TYPES_MASK) {
head = &group->marks_list;
goto clear;
}
*/
mutex_lock_nested(&group->mark_mutex, SINGLE_DEPTH_NESTING);
list_for_each_entry_safe(mark, lmark, &group->marks_list, g_list) {
- if (mark->connector->flags & type)
+ if ((1U << mark->connector->type) & type_mask)
list_move(&mark->g_list, &to_free);
}
mutex_unlock(&group->mark_mutex);
* mtime is the last change of the data within the file. Not changed
* when only metadata is changed, e.g. a rename doesn't affect mtime.
*/
- vi->i_mtime = ntfs2utc(si->last_data_change_time);
+ vi->i_mtime = timespec_to_timespec64(ntfs2utc(si->last_data_change_time));
/*
* ctime is the last change of the metadata of the file. This obviously
* always changes, when mtime is changed. ctime can be changed on its
* own, mtime is then not changed, e.g. when a file is renamed.
*/
- vi->i_ctime = ntfs2utc(si->last_mft_change_time);
+ vi->i_ctime = timespec_to_timespec64(ntfs2utc(si->last_mft_change_time));
/*
* Last access to the data within the file. Not changed during a rename
* for example but changed whenever the file is written to.
*/
- vi->i_atime = ntfs2utc(si->last_access_time);
+ vi->i_atime = timespec_to_timespec64(ntfs2utc(si->last_access_time));
/* Find the attribute list attribute if present. */
ntfs_attr_reinit_search_ctx(ctx);
* for real.
*/
if (!IS_NOCMTIME(VFS_I(base_ni)) && !IS_RDONLY(VFS_I(base_ni))) {
- struct timespec now = current_time(VFS_I(base_ni));
+ struct timespec64 now = current_time(VFS_I(base_ni));
int sync_it = 0;
- if (!timespec_equal(&VFS_I(base_ni)->i_mtime, &now) ||
- !timespec_equal(&VFS_I(base_ni)->i_ctime, &now))
+ if (!timespec64_equal(&VFS_I(base_ni)->i_mtime, &now) ||
+ !timespec64_equal(&VFS_I(base_ni)->i_ctime, &now))
sync_it = 1;
VFS_I(base_ni)->i_mtime = now;
VFS_I(base_ni)->i_ctime = now;
}
}
if (ia_valid & ATTR_ATIME)
- vi->i_atime = timespec_trunc(attr->ia_atime,
- vi->i_sb->s_time_gran);
+ vi->i_atime = timespec64_trunc(attr->ia_atime,
+ vi->i_sb->s_time_gran);
if (ia_valid & ATTR_MTIME)
- vi->i_mtime = timespec_trunc(attr->ia_mtime,
- vi->i_sb->s_time_gran);
+ vi->i_mtime = timespec64_trunc(attr->ia_mtime,
+ vi->i_sb->s_time_gran);
if (ia_valid & ATTR_CTIME)
- vi->i_ctime = timespec_trunc(attr->ia_ctime,
- vi->i_sb->s_time_gran);
+ vi->i_ctime = timespec64_trunc(attr->ia_ctime,
+ vi->i_sb->s_time_gran);
mark_inode_dirty(vi);
out:
return err;
si = (STANDARD_INFORMATION*)((u8*)ctx->attr +
le16_to_cpu(ctx->attr->data.resident.value_offset));
/* Update the access times if they have changed. */
- nt = utc2ntfs(vi->i_mtime);
+ nt = utc2ntfs(timespec64_to_timespec(vi->i_mtime));
if (si->last_data_change_time != nt) {
ntfs_debug("Updating mtime for inode 0x%lx: old = 0x%llx, "
"new = 0x%llx", vi->i_ino, (long long)
si->last_data_change_time = nt;
modified = true;
}
- nt = utc2ntfs(vi->i_ctime);
+ nt = utc2ntfs(timespec64_to_timespec(vi->i_ctime));
if (si->last_mft_change_time != nt) {
ntfs_debug("Updating ctime for inode 0x%lx: old = 0x%llx, "
"new = 0x%llx", vi->i_ino, (long long)
si->last_mft_change_time = nt;
modified = true;
}
- nt = utc2ntfs(vi->i_atime);
+ nt = utc2ntfs(timespec64_to_timespec(vi->i_atime));
if (si->last_access_time != nt) {
ntfs_debug("Updating atime for inode 0x%lx: old = 0x%llx, "
"new = 0x%llx", vi->i_ino,
struct ocfs2_inode_info *oi = OCFS2_I(inode);
struct ocfs2_lock_res *lockres = &oi->ip_inode_lockres;
struct ocfs2_meta_lvb *lvb;
+ struct timespec ts;
lvb = ocfs2_dlm_lvb(&lockres->l_lksb);
lvb->lvb_igid = cpu_to_be32(i_gid_read(inode));
lvb->lvb_imode = cpu_to_be16(inode->i_mode);
lvb->lvb_inlink = cpu_to_be16(inode->i_nlink);
+ ts = timespec64_to_timespec(inode->i_atime);
lvb->lvb_iatime_packed =
- cpu_to_be64(ocfs2_pack_timespec(&inode->i_atime));
+ cpu_to_be64(ocfs2_pack_timespec(&ts));
+ ts = timespec64_to_timespec(inode->i_ctime);
lvb->lvb_ictime_packed =
- cpu_to_be64(ocfs2_pack_timespec(&inode->i_ctime));
+ cpu_to_be64(ocfs2_pack_timespec(&ts));
+ ts = timespec64_to_timespec(inode->i_mtime);
lvb->lvb_imtime_packed =
- cpu_to_be64(ocfs2_pack_timespec(&inode->i_mtime));
+ cpu_to_be64(ocfs2_pack_timespec(&ts));
lvb->lvb_iattr = cpu_to_be32(oi->ip_attr);
lvb->lvb_idynfeatures = cpu_to_be16(oi->ip_dyn_features);
lvb->lvb_igeneration = cpu_to_be32(inode->i_generation);
static void ocfs2_refresh_inode_from_lvb(struct inode *inode)
{
+ struct timespec ts;
struct ocfs2_inode_info *oi = OCFS2_I(inode);
struct ocfs2_lock_res *lockres = &oi->ip_inode_lockres;
struct ocfs2_meta_lvb *lvb;
i_gid_write(inode, be32_to_cpu(lvb->lvb_igid));
inode->i_mode = be16_to_cpu(lvb->lvb_imode);
set_nlink(inode, be16_to_cpu(lvb->lvb_inlink));
- ocfs2_unpack_timespec(&inode->i_atime,
+ ocfs2_unpack_timespec(&ts,
be64_to_cpu(lvb->lvb_iatime_packed));
- ocfs2_unpack_timespec(&inode->i_mtime,
+ inode->i_atime = timespec_to_timespec64(ts);
+ ocfs2_unpack_timespec(&ts,
be64_to_cpu(lvb->lvb_imtime_packed));
- ocfs2_unpack_timespec(&inode->i_ctime,
+ inode->i_mtime = timespec_to_timespec64(ts);
+ ocfs2_unpack_timespec(&ts,
be64_to_cpu(lvb->lvb_ictime_packed));
+ inode->i_ctime = timespec_to_timespec64(ts);
spin_unlock(&oi->ip_lock);
}
int ocfs2_should_update_atime(struct inode *inode,
struct vfsmount *vfsmnt)
{
- struct timespec now;
+ struct timespec64 now;
struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
if (ocfs2_is_hard_readonly(osb) || ocfs2_is_soft_readonly(osb))
return 0;
if (vfsmnt->mnt_flags & MNT_RELATIME) {
- if ((timespec_compare(&inode->i_atime, &inode->i_mtime) <= 0) ||
- (timespec_compare(&inode->i_atime, &inode->i_ctime) <= 0))
+ if ((timespec64_compare(&inode->i_atime, &inode->i_mtime) <= 0) ||
+ (timespec64_compare(&inode->i_atime, &inode->i_ctime) <= 0))
return 1;
return 0;
__s32 count;
};
-static unsigned long translate_dev_map26(unsigned long args, long *error)
-{
- struct ORANGEFS_dev_map_desc32 __user *p32 = (void __user *)args;
- /*
- * Depending on the architecture, allocate some space on the
- * user-call-stack based on our expected layout.
- */
- struct ORANGEFS_dev_map_desc __user *p =
- compat_alloc_user_space(sizeof(*p));
- compat_uptr_t addr;
-
- *error = 0;
- /* get the ptr from the 32 bit user-space */
- if (get_user(addr, &p32->ptr))
- goto err;
- /* try to put that into a 64-bit layout */
- if (put_user(compat_ptr(addr), &p->ptr))
- goto err;
- /* copy the remaining fields */
- if (copy_in_user(&p->total_size, &p32->total_size, sizeof(__s32)))
- goto err;
- if (copy_in_user(&p->size, &p32->size, sizeof(__s32)))
- goto err;
- if (copy_in_user(&p->count, &p32->count, sizeof(__s32)))
- goto err;
- return (unsigned long)p;
-err:
- *error = -EFAULT;
- return 0;
-}
-
/*
* 32 bit user-space apps' ioctl handlers when kernel modules
* is compiled as a 64 bit one
unsigned long args)
{
long ret;
- unsigned long arg = args;
/* Check for properly constructed commands */
ret = check_ioctl_command(cmd);
if (ret < 0)
return ret;
if (cmd == ORANGEFS_DEV_MAP) {
- /*
- * convert the arguments to what we expect internally
- * in kernel space
- */
- arg = translate_dev_map26(args, &ret);
- if (ret < 0) {
- gossip_err("Could not translate dev map\n");
- return ret;
- }
+ struct ORANGEFS_dev_map_desc desc;
+ struct ORANGEFS_dev_map_desc32 d32;
+
+ if (copy_from_user(&d32, (void __user *)args, sizeof(d32)))
+ return -EFAULT;
+
+ desc.ptr = compat_ptr(d32.ptr);
+ desc.total_size = d32.total_size;
+ desc.size = d32.size;
+ desc.count = d32.count;
+ return orangefs_bufmap_initialize(&desc);
}
/* no other ioctl requires translation */
- return dispatch_ioctl_command(cmd, arg);
+ return dispatch_ioctl_command(cmd, args);
}
#endif /* CONFIG_COMPAT is in .config */
return generic_permission(inode, mask);
}
-int orangefs_update_time(struct inode *inode, struct timespec *time, int flags)
+int orangefs_update_time(struct inode *inode, struct timespec64 *time, int flags)
{
struct iattr iattr;
gossip_debug(GOSSIP_INODE_DEBUG, "orangefs_update_time: %pU\n",
int orangefs_permission(struct inode *inode, int mask);
-int orangefs_update_time(struct inode *, struct timespec *, int);
+int orangefs_update_time(struct inode *, struct timespec64 *, int);
/*
* defined in xattr.c
// SPDX-License-Identifier: GPL-2.0
/*
- * Documentation/ABI/stable/orangefs-sysfs:
+ * Documentation/ABI/stable/sysfs-fs-orangefs:
*
* What: /sys/fs/orangefs/perf_counter_reset
* Date: June 2015
return err;
}
-int ovl_update_time(struct inode *inode, struct timespec *ts, int flags)
+int ovl_update_time(struct inode *inode, struct timespec64 *ts, int flags)
{
if (flags & S_ATIME) {
struct ovl_fs *ofs = inode->i_sb->s_fs_info;
ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size);
struct posix_acl *ovl_get_acl(struct inode *inode, int type);
int ovl_open_maybe_copy_up(struct dentry *dentry, unsigned int file_flags);
-int ovl_update_time(struct inode *inode, struct timespec *ts, int flags);
+int ovl_update_time(struct inode *inode, struct timespec64 *ts, int flags);
bool ovl_is_private_xattr(const char *name);
struct ovl_inode_params {
if (env_start != arg_end || env_start >= env_end)
env_start = env_end = arg_end;
+ /* .. and limit it to a maximum of one page of slop */
+ if (env_end >= arg_end + PAGE_SIZE)
+ env_end = arg_end + PAGE_SIZE - 1;
+
/* We're not going to care if "*ppos" has high bits set */
pos = arg_start + *ppos;
while (count) {
int got;
size_t size = min_t(size_t, PAGE_SIZE, count);
+ long offset;
- got = access_remote_vm(mm, pos, page, size, FOLL_ANON);
- if (got <= 0)
+ /*
+ * Are we already starting past the official end?
+ * We always include the last byte that is *supposed*
+ * to be NUL
+ */
+ offset = (pos >= arg_end) ? pos - arg_end + 1 : 0;
+
+ got = access_remote_vm(mm, pos - offset, page, size + offset, FOLL_ANON);
+ if (got <= offset)
break;
+ got -= offset;
/* Don't walk past a NUL character once you hit arg_end */
if (pos + got >= arg_end) {
n = arg_end - pos - 1;
/* Cut off at first NUL after 'n' */
- got = n + strnlen(page+n, got-n);
- if (!got)
+ got = n + strnlen(page+n, offset+got-n);
+ if (got < offset)
break;
+ got -= offset;
+
+ /* Include the NUL if it existed */
+ if (got < size)
+ got++;
}
- got -= copy_to_user(buf, page, got);
+ got -= copy_to_user(buf, page+offset, got);
if (unlikely(!got)) {
if (!len)
len = -EFAULT;
for (p = ents; p < last; p++) {
if (p->len != dentry->d_name.len)
continue;
- if (!memcmp(dentry->d_name.name, p->name, p->len))
+ if (!memcmp(dentry->d_name.name, p->name, p->len)) {
+ res = proc_pident_instantiate(dentry, task, p);
break;
+ }
}
- if (p >= last)
- goto out;
-
- res = proc_pident_instantiate(dentry, task, p);
-out:
put_task_struct(task);
out_no_task:
return res;
if (!ent)
goto out;
- if (qstr.len + 1 <= sizeof(ent->inline_name)) {
+ if (qstr.len + 1 <= SIZEOF_PDE_INLINE_NAME) {
ent->name = ent->inline_name;
} else {
ent->name = kmalloc(qstr.len + 1, GFP_KERNEL);
return __PDE_DATA(inode);
}
EXPORT_SYMBOL(PDE_DATA);
+
+/*
+ * Pull a user buffer into memory and pass it to the file's write handler if
+ * one is supplied. The ->write() method is permitted to modify the
+ * kernel-side buffer.
+ */
+ssize_t proc_simple_write(struct file *f, const char __user *ubuf, size_t size,
+ loff_t *_pos)
+{
+ struct proc_dir_entry *pde = PDE(file_inode(f));
+ char *buf;
+ int ret;
+
+ if (!pde->write)
+ return -EACCES;
+ if (size == 0 || size > PAGE_SIZE - 1)
+ return -EINVAL;
+ buf = memdup_user_nul(ubuf, size);
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
+ ret = pde->write(f, buf, size);
+ kfree(buf);
+ return ret == 0 ? size : ret;
+}
kmem_cache_create("pde_opener", sizeof(struct pde_opener), 0,
SLAB_ACCOUNT|SLAB_PANIC, NULL);
proc_dir_entry_cache = kmem_cache_create_usercopy(
- "proc_dir_entry", sizeof(struct proc_dir_entry), 0, SLAB_PANIC,
- offsetof(struct proc_dir_entry, inline_name),
- sizeof_field(struct proc_dir_entry, inline_name), NULL);
+ "proc_dir_entry", SIZEOF_PDE_SLOT, 0, SLAB_PANIC,
+ OFFSETOF_PDE_NAME, SIZEOF_PDE_INLINE_NAME, NULL);
}
static int proc_show_options(struct seq_file *seq, struct dentry *root)
const struct seq_operations *seq_ops;
int (*single_show)(struct seq_file *, void *);
};
+ proc_write_t write;
void *data;
unsigned int state_size;
unsigned int low_ino;
char *name;
umode_t mode;
u8 namelen;
-#ifdef CONFIG_64BIT
-#define SIZEOF_PDE_INLINE_NAME (192-155)
-#else
-#define SIZEOF_PDE_INLINE_NAME (128-95)
-#endif
- char inline_name[SIZEOF_PDE_INLINE_NAME];
+ char inline_name[];
} __randomize_layout;
+#define OFFSETOF_PDE_NAME offsetof(struct proc_dir_entry, inline_name)
+#define SIZEOF_PDE_SLOT \
+ (OFFSETOF_PDE_NAME + 34 <= 64 ? 64 : \
+ OFFSETOF_PDE_NAME + 34 <= 128 ? 128 : \
+ OFFSETOF_PDE_NAME + 34 <= 192 ? 192 : \
+ OFFSETOF_PDE_NAME + 34 <= 256 ? 256 : \
+ OFFSETOF_PDE_NAME + 34 <= 512 ? 512 : \
+ 0)
+
+#define SIZEOF_PDE_INLINE_NAME (SIZEOF_PDE_SLOT - OFFSETOF_PDE_NAME)
+
extern struct kmem_cache *proc_dir_entry_cache;
void pde_free(struct proc_dir_entry *pde);
{
return S_ISDIR(pde->mode) && !pde->proc_iops;
}
+extern ssize_t proc_simple_write(struct file *, const char __user *, size_t, loff_t *);
/*
* inode.c
WARN_ON_ONCE(state_size < sizeof(*p));
+ if (file->f_mode & FMODE_WRITE && !PDE(inode)->write)
+ return -EACCES;
+
net = get_proc_net(inode);
if (!net)
return -ENXIO;
static const struct file_operations proc_net_seq_fops = {
.open = seq_open_net,
.read = seq_read,
+ .write = proc_simple_write,
.llseek = seq_lseek,
.release = seq_release_net,
};
}
EXPORT_SYMBOL_GPL(proc_create_net_data);
+/**
+ * proc_create_net_data_write - Create a writable net_ns-specific proc file
+ * @name: The name of the file.
+ * @mode: The file's access mode.
+ * @parent: The parent directory in which to create.
+ * @ops: The seq_file ops with which to read the file.
+ * @write: The write method which which to 'modify' the file.
+ * @data: Data for retrieval by PDE_DATA().
+ *
+ * Create a network namespaced proc file in the @parent directory with the
+ * specified @name and @mode that allows reading of a file that displays a
+ * series of elements and also provides for the file accepting writes that have
+ * some arbitrary effect.
+ *
+ * The functions in the @ops table are used to iterate over items to be
+ * presented and extract the readable content using the seq_file interface.
+ *
+ * The @write function is called with the data copied into a kernel space
+ * scratch buffer and has a NUL appended for convenience. The buffer may be
+ * modified by the @write function. @write should return 0 on success.
+ *
+ * The @data value is accessible from the @show and @write functions by calling
+ * PDE_DATA() on the file inode. The network namespace must be accessed by
+ * calling seq_file_net() on the seq_file struct.
+ */
+struct proc_dir_entry *proc_create_net_data_write(const char *name, umode_t mode,
+ struct proc_dir_entry *parent,
+ const struct seq_operations *ops,
+ proc_write_t write,
+ unsigned int state_size, void *data)
+{
+ struct proc_dir_entry *p;
+
+ p = proc_create_reg(name, mode, &parent, data);
+ if (!p)
+ return NULL;
+ p->proc_fops = &proc_net_seq_fops;
+ p->seq_ops = ops;
+ p->state_size = state_size;
+ p->write = write;
+ return proc_register(parent, p);
+}
+EXPORT_SYMBOL_GPL(proc_create_net_data_write);
+
static int single_open_net(struct inode *inode, struct file *file)
{
struct proc_dir_entry *de = PDE(inode);
static const struct file_operations proc_net_single_fops = {
.open = single_open_net,
.read = seq_read,
+ .write = proc_simple_write,
.llseek = seq_lseek,
.release = single_release_net,
};
}
EXPORT_SYMBOL_GPL(proc_create_net_single);
+/**
+ * proc_create_net_single_write - Create a writable net_ns-specific proc file
+ * @name: The name of the file.
+ * @mode: The file's access mode.
+ * @parent: The parent directory in which to create.
+ * @show: The seqfile show method with which to read the file.
+ * @write: The write method which which to 'modify' the file.
+ * @data: Data for retrieval by PDE_DATA().
+ *
+ * Create a network-namespaced proc file in the @parent directory with the
+ * specified @name and @mode that allows reading of a file that displays a
+ * single element rather than a series and also provides for the file accepting
+ * writes that have some arbitrary effect.
+ *
+ * The @show function is called to extract the readable content via the
+ * seq_file interface.
+ *
+ * The @write function is called with the data copied into a kernel space
+ * scratch buffer and has a NUL appended for convenience. The buffer may be
+ * modified by the @write function. @write should return 0 on success.
+ *
+ * The @data value is accessible from the @show and @write functions by calling
+ * PDE_DATA() on the file inode. The network namespace must be accessed by
+ * calling seq_file_single_net() on the seq_file struct.
+ */
+struct proc_dir_entry *proc_create_net_single_write(const char *name, umode_t mode,
+ struct proc_dir_entry *parent,
+ int (*show)(struct seq_file *, void *),
+ proc_write_t write,
+ void *data)
+{
+ struct proc_dir_entry *p;
+
+ p = proc_create_reg(name, mode, &parent, data);
+ if (!p)
+ return NULL;
+ p->proc_fops = &proc_net_single_fops;
+ p->single_show = show;
+ p->write = write;
+ return proc_register(parent, p);
+}
+EXPORT_SYMBOL_GPL(proc_create_net_single_write);
+
static struct net *get_proc_task_net(struct inode *dir)
{
struct task_struct *task;
.proc_fops = &proc_root_operations,
.parent = &proc_root,
.subdir = RB_ROOT,
- .name = proc_root.inline_name,
- .inline_name = "/proc",
+ .name = "/proc",
};
int pid_ns_prepare_proc(struct pid_namespace *ns)
static int uptime_proc_show(struct seq_file *m, void *v)
{
struct timespec uptime;
- struct timespec idle;
+ struct timespec64 idle;
u64 nsec;
u32 rem;
int i;
record->psi = psinfo;
/* Report zeroed timestamp if called before timekeeping has resumed. */
- record->time = ns_to_timespec(ktime_get_real_fast_ns());
+ record->time = ns_to_timespec64(ktime_get_real_fast_ns());
}
/*
return prz;
}
-static int ramoops_read_kmsg_hdr(char *buffer, struct timespec *time,
+static int ramoops_read_kmsg_hdr(char *buffer, struct timespec64 *time,
bool *compressed)
{
char data_type;
int header_length = 0;
- if (sscanf(buffer, RAMOOPS_KERNMSG_HDR "%lu.%lu-%c\n%n", &time->tv_sec,
- &time->tv_nsec, &data_type, &header_length) == 3) {
+ if (sscanf(buffer, RAMOOPS_KERNMSG_HDR "%lld.%lu-%c\n%n",
+ (time64_t *)&time->tv_sec, &time->tv_nsec, &data_type,
+ &header_length) == 3) {
if (data_type == 'C')
*compressed = true;
else
*compressed = false;
- } else if (sscanf(buffer, RAMOOPS_KERNMSG_HDR "%lu.%lu\n%n",
- &time->tv_sec, &time->tv_nsec, &header_length) == 2) {
- *compressed = false;
+ } else if (sscanf(buffer, RAMOOPS_KERNMSG_HDR "%lld.%lu\n%n",
+ (time64_t *)&time->tv_sec, &time->tv_nsec,
+ &header_length) == 2) {
+ *compressed = false;
} else {
time->tv_sec = 0;
time->tv_nsec = 0;
char *hdr;
size_t len;
- hdr = kasprintf(GFP_ATOMIC, RAMOOPS_KERNMSG_HDR "%lu.%lu-%c\n",
- record->time.tv_sec,
+ hdr = kasprintf(GFP_ATOMIC, RAMOOPS_KERNMSG_HDR "%lld.%06lu-%c\n",
+ (time64_t)record->time.tv_sec,
record->time.tv_nsec / 1000,
record->compressed ? 'C' : 'D');
WARN_ON_ONCE(!hdr);
int jbegin_count;
umode_t old_inode_mode;
unsigned long savelink = 1;
- struct timespec ctime;
+ struct timespec64 ctime;
if (flags & ~RENAME_NOREPLACE)
return -EINVAL;
static void update_ctime(struct inode *inode)
{
- struct timespec now = current_time(inode);
+ struct timespec64 now = current_time(inode);
if (inode_unhashed(inode) || !inode->i_nlink ||
- timespec_equal(&inode->i_ctime, &now))
+ timespec64_equal(&inode->i_ctime, &now))
return;
inode->i_ctime = current_time(inode);
.llseek = noop_llseek,
};
-static int do_signalfd4(int ufd, sigset_t __user *user_mask, size_t sizemask,
- int flags)
+static int do_signalfd4(int ufd, sigset_t *mask, int flags)
{
- sigset_t sigmask;
struct signalfd_ctx *ctx;
/* Check the SFD_* constants for consistency. */
if (flags & ~(SFD_CLOEXEC | SFD_NONBLOCK))
return -EINVAL;
- if (sizemask != sizeof(sigset_t) ||
- copy_from_user(&sigmask, user_mask, sizeof(sigmask)))
- return -EINVAL;
- sigdelsetmask(&sigmask, sigmask(SIGKILL) | sigmask(SIGSTOP));
- signotset(&sigmask);
+ sigdelsetmask(mask, sigmask(SIGKILL) | sigmask(SIGSTOP));
+ signotset(mask);
if (ufd == -1) {
ctx = kmalloc(sizeof(*ctx), GFP_KERNEL);
if (!ctx)
return -ENOMEM;
- ctx->sigmask = sigmask;
+ ctx->sigmask = *mask;
/*
* When we call this, the initialization must be complete, since
return -EINVAL;
}
spin_lock_irq(¤t->sighand->siglock);
- ctx->sigmask = sigmask;
+ ctx->sigmask = *mask;
spin_unlock_irq(¤t->sighand->siglock);
wake_up(¤t->sighand->signalfd_wqh);
SYSCALL_DEFINE4(signalfd4, int, ufd, sigset_t __user *, user_mask,
size_t, sizemask, int, flags)
{
- return do_signalfd4(ufd, user_mask, sizemask, flags);
+ sigset_t mask;
+
+ if (sizemask != sizeof(sigset_t) ||
+ copy_from_user(&mask, user_mask, sizeof(mask)))
+ return -EINVAL;
+ return do_signalfd4(ufd, &mask, flags);
}
SYSCALL_DEFINE3(signalfd, int, ufd, sigset_t __user *, user_mask,
size_t, sizemask)
{
- return do_signalfd4(ufd, user_mask, sizemask, 0);
+ sigset_t mask;
+
+ if (sizemask != sizeof(sigset_t) ||
+ copy_from_user(&mask, user_mask, sizeof(mask)))
+ return -EINVAL;
+ return do_signalfd4(ufd, &mask, 0);
}
#ifdef CONFIG_COMPAT
static long do_compat_signalfd4(int ufd,
- const compat_sigset_t __user *sigmask,
+ const compat_sigset_t __user *user_mask,
compat_size_t sigsetsize, int flags)
{
- sigset_t tmp;
- sigset_t __user *ksigmask;
+ sigset_t mask;
if (sigsetsize != sizeof(compat_sigset_t))
return -EINVAL;
- if (get_compat_sigset(&tmp, sigmask))
- return -EFAULT;
- ksigmask = compat_alloc_user_space(sizeof(sigset_t));
- if (copy_to_user(ksigmask, &tmp, sizeof(sigset_t)))
+ if (get_compat_sigset(&mask, user_mask))
return -EFAULT;
-
- return do_signalfd4(ufd, ksigmask, sizeof(sigset_t), flags);
+ return do_signalfd4(ufd, &mask, flags);
}
COMPAT_SYSCALL_DEFINE4(signalfd4, int, ufd,
- const compat_sigset_t __user *, sigmask,
+ const compat_sigset_t __user *, user_mask,
compat_size_t, sigsetsize,
int, flags)
{
- return do_compat_signalfd4(ufd, sigmask, sigsetsize, flags);
+ return do_compat_signalfd4(ufd, user_mask, sigsetsize, flags);
}
COMPAT_SYSCALL_DEFINE3(signalfd, int, ufd,
- const compat_sigset_t __user *,sigmask,
+ const compat_sigset_t __user *, user_mask,
compat_size_t, sigsetsize)
{
- return do_compat_signalfd4(ufd, sigmask, sigsetsize, 0);
+ return do_compat_signalfd4(ufd, user_mask, sigsetsize, 0);
}
#endif
* For lack of a better implementation, implement vmsplice() to userspace
* as a simple copy of the pipes pages to the user iov.
*/
-static long vmsplice_to_user(struct file *file, const struct iovec __user *uiov,
- unsigned long nr_segs, unsigned int flags)
+static long vmsplice_to_user(struct file *file, struct iov_iter *iter,
+ unsigned int flags)
{
- struct pipe_inode_info *pipe;
- struct splice_desc sd;
- long ret;
- struct iovec iovstack[UIO_FASTIOV];
- struct iovec *iov = iovstack;
- struct iov_iter iter;
+ struct pipe_inode_info *pipe = get_pipe_info(file);
+ struct splice_desc sd = {
+ .total_len = iov_iter_count(iter),
+ .flags = flags,
+ .u.data = iter
+ };
+ long ret = 0;
- pipe = get_pipe_info(file);
if (!pipe)
return -EBADF;
- ret = import_iovec(READ, uiov, nr_segs,
- ARRAY_SIZE(iovstack), &iov, &iter);
- if (ret < 0)
- return ret;
-
- sd.total_len = iov_iter_count(&iter);
- sd.len = 0;
- sd.flags = flags;
- sd.u.data = &iter;
- sd.pos = 0;
-
if (sd.total_len) {
pipe_lock(pipe);
ret = __splice_from_pipe(pipe, &sd, pipe_to_user);
pipe_unlock(pipe);
}
- kfree(iov);
return ret;
}
* as splice-from-memory, where the regular splice is splice-from-file (or
* to file). In both cases the output is a pipe, naturally.
*/
-static long vmsplice_to_pipe(struct file *file, const struct iovec __user *uiov,
- unsigned long nr_segs, unsigned int flags)
+static long vmsplice_to_pipe(struct file *file, struct iov_iter *iter,
+ unsigned int flags)
{
struct pipe_inode_info *pipe;
- struct iovec iovstack[UIO_FASTIOV];
- struct iovec *iov = iovstack;
- struct iov_iter from;
- long ret;
+ long ret = 0;
unsigned buf_flag = 0;
if (flags & SPLICE_F_GIFT)
if (!pipe)
return -EBADF;
- ret = import_iovec(WRITE, uiov, nr_segs,
- ARRAY_SIZE(iovstack), &iov, &from);
- if (ret < 0)
- return ret;
-
pipe_lock(pipe);
ret = wait_for_space(pipe, flags);
if (!ret)
- ret = iter_to_pipe(&from, pipe, buf_flag);
+ ret = iter_to_pipe(iter, pipe, buf_flag);
pipe_unlock(pipe);
if (ret > 0)
wakeup_pipe_readers(pipe);
- kfree(iov);
return ret;
}
+static int vmsplice_type(struct fd f, int *type)
+{
+ if (!f.file)
+ return -EBADF;
+ if (f.file->f_mode & FMODE_WRITE) {
+ *type = WRITE;
+ } else if (f.file->f_mode & FMODE_READ) {
+ *type = READ;
+ } else {
+ fdput(f);
+ return -EBADF;
+ }
+ return 0;
+}
+
/*
* Note that vmsplice only really supports true splicing _from_ user memory
* to a pipe, not the other way around. Splicing from user memory is a simple
* Currently we punt and implement it as a normal copy, see pipe_to_user().
*
*/
-static long do_vmsplice(int fd, const struct iovec __user *iov,
- unsigned long nr_segs, unsigned int flags)
+static long do_vmsplice(struct file *f, struct iov_iter *iter, unsigned int flags)
{
- struct fd f;
- long error;
-
if (unlikely(flags & ~SPLICE_F_ALL))
return -EINVAL;
- if (unlikely(nr_segs > UIO_MAXIOV))
- return -EINVAL;
- else if (unlikely(!nr_segs))
- return 0;
- error = -EBADF;
- f = fdget(fd);
- if (f.file) {
- if (f.file->f_mode & FMODE_WRITE)
- error = vmsplice_to_pipe(f.file, iov, nr_segs, flags);
- else if (f.file->f_mode & FMODE_READ)
- error = vmsplice_to_user(f.file, iov, nr_segs, flags);
-
- fdput(f);
- }
+ if (!iov_iter_count(iter))
+ return 0;
- return error;
+ if (iov_iter_rw(iter) == WRITE)
+ return vmsplice_to_pipe(f, iter, flags);
+ else
+ return vmsplice_to_user(f, iter, flags);
}
-SYSCALL_DEFINE4(vmsplice, int, fd, const struct iovec __user *, iov,
+SYSCALL_DEFINE4(vmsplice, int, fd, const struct iovec __user *, uiov,
unsigned long, nr_segs, unsigned int, flags)
{
- return do_vmsplice(fd, iov, nr_segs, flags);
+ struct iovec iovstack[UIO_FASTIOV];
+ struct iovec *iov = iovstack;
+ struct iov_iter iter;
+ long error;
+ struct fd f;
+ int type;
+
+ f = fdget(fd);
+ error = vmsplice_type(f, &type);
+ if (error)
+ return error;
+
+ error = import_iovec(type, uiov, nr_segs,
+ ARRAY_SIZE(iovstack), &iov, &iter);
+ if (!error) {
+ error = do_vmsplice(f.file, &iter, flags);
+ kfree(iov);
+ }
+ fdput(f);
+ return error;
}
#ifdef CONFIG_COMPAT
COMPAT_SYSCALL_DEFINE4(vmsplice, int, fd, const struct compat_iovec __user *, iov32,
unsigned int, nr_segs, unsigned int, flags)
{
- unsigned i;
- struct iovec __user *iov;
- if (nr_segs > UIO_MAXIOV)
- return -EINVAL;
- iov = compat_alloc_user_space(nr_segs * sizeof(struct iovec));
- for (i = 0; i < nr_segs; i++) {
- struct compat_iovec v;
- if (get_user(v.iov_base, &iov32[i].iov_base) ||
- get_user(v.iov_len, &iov32[i].iov_len) ||
- put_user(compat_ptr(v.iov_base), &iov[i].iov_base) ||
- put_user(v.iov_len, &iov[i].iov_len))
- return -EFAULT;
+ struct iovec iovstack[UIO_FASTIOV];
+ struct iovec *iov = iovstack;
+ struct iov_iter iter;
+ long error;
+ struct fd f;
+ int type;
+
+ f = fdget(fd);
+ error = vmsplice_type(f, &type);
+ if (error)
+ return error;
+
+ error = compat_import_iovec(type, iov32, nr_segs,
+ ARRAY_SIZE(iovstack), &iov, &iter);
+ if (!error) {
+ error = do_vmsplice(f.file, &iter, flags);
+ kfree(iov);
}
- return do_vmsplice(fd, iov, nr_segs, flags);
+ fdput(f);
+ return error;
}
#endif
.dirtied_ino = 3 };
struct ubifs_budget_req ino_req = { .dirtied_ino = 1,
.dirtied_ino_d = ALIGN(old_inode_ui->data_len, 8) };
- struct timespec time;
+ struct timespec64 time;
unsigned int uninitialized_var(saved_nlink);
struct fscrypt_name old_nm, new_nm;
int sync = IS_DIRSYNC(old_dir) || IS_DIRSYNC(new_dir);
struct inode *fst_inode = d_inode(old_dentry);
struct inode *snd_inode = d_inode(new_dentry);
- struct timespec time;
+ struct timespec64 time;
int err;
struct fscrypt_name fst_nm, snd_nm;
if (attr->ia_valid & ATTR_GID)
inode->i_gid = attr->ia_gid;
if (attr->ia_valid & ATTR_ATIME)
- inode->i_atime = timespec_trunc(attr->ia_atime,
- inode->i_sb->s_time_gran);
+ inode->i_atime = timespec64_trunc(attr->ia_atime,
+ inode->i_sb->s_time_gran);
if (attr->ia_valid & ATTR_MTIME)
- inode->i_mtime = timespec_trunc(attr->ia_mtime,
- inode->i_sb->s_time_gran);
+ inode->i_mtime = timespec64_trunc(attr->ia_mtime,
+ inode->i_sb->s_time_gran);
if (attr->ia_valid & ATTR_CTIME)
- inode->i_ctime = timespec_trunc(attr->ia_ctime,
- inode->i_sb->s_time_gran);
+ inode->i_ctime = timespec64_trunc(attr->ia_ctime,
+ inode->i_sb->s_time_gran);
if (attr->ia_valid & ATTR_MODE) {
umode_t mode = attr->ia_mode;
static inline int mctime_update_needed(const struct inode *inode,
const struct timespec *now)
{
- if (!timespec_equal(&inode->i_mtime, now) ||
- !timespec_equal(&inode->i_ctime, now))
+ struct timespec64 now64 = timespec_to_timespec64(*now);
+ if (!timespec64_equal(&inode->i_mtime, &now64) ||
+ !timespec64_equal(&inode->i_ctime, &now64))
return 1;
return 0;
}
*
* This function updates time of the inode.
*/
-int ubifs_update_time(struct inode *inode, struct timespec *time,
+int ubifs_update_time(struct inode *inode, struct timespec64 *time,
int flags)
{
struct ubifs_inode *ui = ubifs_inode(inode);
*/
static int update_mctime(struct inode *inode)
{
- struct timespec now = current_time(inode);
+ struct timespec now = timespec64_to_timespec(current_time(inode));
struct ubifs_inode *ui = ubifs_inode(inode);
struct ubifs_info *c = inode->i_sb->s_fs_info;
struct page *page = vmf->page;
struct inode *inode = file_inode(vmf->vma->vm_file);
struct ubifs_info *c = inode->i_sb->s_fs_info;
- struct timespec now = current_time(inode);
+ struct timespec now = timespec64_to_timespec(current_time(inode));
struct ubifs_budget_req req = { .new_page = 1 };
int err, update_time;
int ubifs_fsync(struct file *file, loff_t start, loff_t end, int datasync);
int ubifs_setattr(struct dentry *dentry, struct iattr *attr);
#ifdef CONFIG_UBIFS_ATIME_SUPPORT
-int ubifs_update_time(struct inode *inode, struct timespec *time, int flags);
+int ubifs_update_time(struct inode *inode, struct timespec64 *time, int flags);
#endif
/* dir.c */
iinfo->i_alloc_type = ICBTAG_FLAG_AD_SHORT;
else
iinfo->i_alloc_type = ICBTAG_FLAG_AD_LONG;
- inode->i_mtime = inode->i_atime = inode->i_ctime =
- iinfo->i_crtime = current_time(inode);
+ inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode);
+ iinfo->i_crtime = timespec64_to_timespec(inode->i_mtime);
if (unlikely(insert_inode_locked(inode) < 0)) {
make_bad_inode(inode);
iput(inode);
struct udf_inode_info *iinfo = UDF_I(inode);
struct udf_sb_info *sbi = UDF_SB(inode->i_sb);
struct kernel_lb_addr *iloc = &iinfo->i_location;
+ struct timespec ts;
unsigned int link_count;
unsigned int indirections = 0;
int bs = inode->i_sb->s_blocksize;
inode->i_blocks = le64_to_cpu(fe->logicalBlocksRecorded) <<
(inode->i_sb->s_blocksize_bits - 9);
- if (!udf_disk_stamp_to_time(&inode->i_atime, fe->accessTime))
- inode->i_atime = sbi->s_record_time;
-
- if (!udf_disk_stamp_to_time(&inode->i_mtime,
- fe->modificationTime))
- inode->i_mtime = sbi->s_record_time;
-
- if (!udf_disk_stamp_to_time(&inode->i_ctime, fe->attrTime))
- inode->i_ctime = sbi->s_record_time;
+ udf_disk_stamp_to_time(&ts, fe->accessTime);
+ inode->i_atime = timespec_to_timespec64(ts);
+ udf_disk_stamp_to_time(&ts, fe->modificationTime);
+ inode->i_mtime = timespec_to_timespec64(ts);
+ udf_disk_stamp_to_time(&ts, fe->attrTime);
+ inode->i_ctime = timespec_to_timespec64(ts);
iinfo->i_unique = le64_to_cpu(fe->uniqueID);
iinfo->i_lenEAttr = le32_to_cpu(fe->lengthExtendedAttr);
inode->i_blocks = le64_to_cpu(efe->logicalBlocksRecorded) <<
(inode->i_sb->s_blocksize_bits - 9);
- if (!udf_disk_stamp_to_time(&inode->i_atime, efe->accessTime))
- inode->i_atime = sbi->s_record_time;
-
- if (!udf_disk_stamp_to_time(&inode->i_mtime,
- efe->modificationTime))
- inode->i_mtime = sbi->s_record_time;
-
- if (!udf_disk_stamp_to_time(&iinfo->i_crtime, efe->createTime))
- iinfo->i_crtime = sbi->s_record_time;
-
- if (!udf_disk_stamp_to_time(&inode->i_ctime, efe->attrTime))
- inode->i_ctime = sbi->s_record_time;
+ udf_disk_stamp_to_time(&ts, efe->accessTime);
+ inode->i_atime = timespec_to_timespec64(ts);
+ udf_disk_stamp_to_time(&ts, efe->modificationTime);
+ inode->i_mtime = timespec_to_timespec64(ts);
+ udf_disk_stamp_to_time(&iinfo->i_crtime, efe->createTime);
+ udf_disk_stamp_to_time(&ts, efe->attrTime);
+ inode->i_ctime = timespec_to_timespec64(ts);
iinfo->i_unique = le64_to_cpu(efe->uniqueID);
iinfo->i_lenEAttr = le32_to_cpu(efe->lengthExtendedAttr);
inode->i_sb->s_blocksize - sizeof(struct fileEntry));
fe->logicalBlocksRecorded = cpu_to_le64(lb_recorded);
- udf_time_to_disk_stamp(&fe->accessTime, inode->i_atime);
- udf_time_to_disk_stamp(&fe->modificationTime, inode->i_mtime);
- udf_time_to_disk_stamp(&fe->attrTime, inode->i_ctime);
+ udf_time_to_disk_stamp(&fe->accessTime,
+ timespec64_to_timespec(inode->i_atime));
+ udf_time_to_disk_stamp(&fe->modificationTime,
+ timespec64_to_timespec(inode->i_mtime));
+ udf_time_to_disk_stamp(&fe->attrTime,
+ timespec64_to_timespec(inode->i_ctime));
memset(&(fe->impIdent), 0, sizeof(struct regid));
strcpy(fe->impIdent.ident, UDF_ID_DEVELOPER);
fe->impIdent.identSuffix[0] = UDF_OS_CLASS_UNIX;
efe->objectSize = cpu_to_le64(inode->i_size);
efe->logicalBlocksRecorded = cpu_to_le64(lb_recorded);
- udf_adjust_time(iinfo, inode->i_atime);
- udf_adjust_time(iinfo, inode->i_mtime);
- udf_adjust_time(iinfo, inode->i_ctime);
+ udf_adjust_time(iinfo, timespec64_to_timespec(inode->i_atime));
+ udf_adjust_time(iinfo, timespec64_to_timespec(inode->i_mtime));
+ udf_adjust_time(iinfo, timespec64_to_timespec(inode->i_ctime));
- udf_time_to_disk_stamp(&efe->accessTime, inode->i_atime);
- udf_time_to_disk_stamp(&efe->modificationTime, inode->i_mtime);
+ udf_time_to_disk_stamp(&efe->accessTime,
+ timespec64_to_timespec(inode->i_atime));
+ udf_time_to_disk_stamp(&efe->modificationTime,
+ timespec64_to_timespec(inode->i_mtime));
udf_time_to_disk_stamp(&efe->createTime, iinfo->i_crtime);
- udf_time_to_disk_stamp(&efe->attrTime, inode->i_ctime);
+ udf_time_to_disk_stamp(&efe->attrTime,
+ timespec64_to_timespec(inode->i_ctime));
memset(&(efe->impIdent), 0, sizeof(efe->impIdent));
strcpy(efe->impIdent.ident, UDF_ID_DEVELOPER);
struct buffer_head *bh;
uint16_t ident;
int ret = -ENOMEM;
+#ifdef UDFFS_DEBUG
+ struct timestamp *ts;
+#endif
outstr = kmalloc(128, GFP_NOFS);
if (!outstr)
pvoldesc = (struct primaryVolDesc *)bh->b_data;
- if (udf_disk_stamp_to_time(&UDF_SB(sb)->s_record_time,
- pvoldesc->recordingDateAndTime)) {
+ udf_disk_stamp_to_time(&UDF_SB(sb)->s_record_time,
+ pvoldesc->recordingDateAndTime);
#ifdef UDFFS_DEBUG
- struct timestamp *ts = &pvoldesc->recordingDateAndTime;
- udf_debug("recording time %04u/%02u/%02u %02u:%02u (%x)\n",
- le16_to_cpu(ts->year), ts->month, ts->day, ts->hour,
- ts->minute, le16_to_cpu(ts->typeAndTimezone));
+ ts = &pvoldesc->recordingDateAndTime;
+ udf_debug("recording time %04u/%02u/%02u %02u:%02u (%x)\n",
+ le16_to_cpu(ts->year), ts->month, ts->day, ts->hour,
+ ts->minute, le16_to_cpu(ts->typeAndTimezone));
#endif
- }
+
ret = udf_dstrCS0toChar(sb, outstr, 31, pvoldesc->volIdent, 32);
if (ret < 0)
extern struct short_ad *udf_get_fileshortad(uint8_t *, int, uint32_t *, int);
/* udftime.c */
-extern struct timespec *udf_disk_stamp_to_time(struct timespec *dest,
+extern void udf_disk_stamp_to_time(struct timespec *dest,
struct timestamp src);
-extern struct timestamp *udf_time_to_disk_stamp(struct timestamp *dest, struct timespec src);
+extern void udf_time_to_disk_stamp(struct timestamp *dest, struct timespec src);
#endif /* __UDF_DECL_H */
#include <linux/kernel.h>
#include <linux/time.h>
-struct timespec *
+void
udf_disk_stamp_to_time(struct timespec *dest, struct timestamp src)
{
u16 typeAndTimezone = le16_to_cpu(src.typeAndTimezone);
* recorded with bogus sub-second values.
*/
dest->tv_nsec %= NSEC_PER_SEC;
- return dest;
}
-struct timestamp *
+void
udf_time_to_disk_stamp(struct timestamp *dest, struct timespec ts)
{
long seconds;
offset = -sys_tz.tz_minuteswest;
- if (!dest)
- return NULL;
-
dest->typeAndTimezone = cpu_to_le16(0x1000 | (offset & 0x0FFF));
seconds = ts.tv_sec + offset * 60;
dest->centiseconds * 10000) / 100;
dest->microseconds = (ts.tv_nsec / 1000 - dest->centiseconds * 10000 -
dest->hundredsOfMicroseconds * 100);
- return dest;
}
/* EOF */
xfs_inode_t *ip;
uint flags;
int error;
- struct timespec tv;
+ struct timespec64 tv;
struct inode *inode;
/*
STATIC int
xfs_vn_update_time(
struct inode *inode,
- struct timespec *now,
+ struct timespec64 *now,
int flags)
{
struct xfs_inode *ip = XFS_I(inode);
int flags)
{
struct inode *inode = VFS_I(ip);
- struct timespec tv;
+ struct timespec64 tv;
ASSERT(tp);
ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL));
/* Asymmetric public-key cryptography key subtype
*
- * See Documentation/security/asymmetric-keys.txt
+ * See Documentation/crypto/asymmetric-keys.txt
*
* Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
/* Asymmetric Public-key cryptography key type interface
*
- * See Documentation/security/asymmetric-keys.txt
+ * See Documentation/crypto/asymmetric-keys.txt
*
* Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
/* Generic associative array implementation.
*
- * See Documentation/assoc_array.txt for information.
+ * See Documentation/core-api/assoc_array.rst for information.
*
* Copyright (C) 2013 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
/* Private definitions for the generic associative array implementation.
*
- * See Documentation/assoc_array.txt for information.
+ * See Documentation/core-api/assoc_array.rst for information.
*
* Copyright (C) 2013 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
void blk_mq_freeze_queue_wait(struct request_queue *q);
int blk_mq_freeze_queue_wait_timeout(struct request_queue *q,
unsigned long timeout);
-int blk_mq_tagset_iter(struct blk_mq_tag_set *set, void *data,
- int (reinit_request)(void *, struct request *));
int blk_mq_map_queues(struct blk_mq_tag_set *set);
void blk_mq_update_nr_hw_queues(struct blk_mq_tag_set *set, int nr_hw_queues);
#define RQF_ZONE_WRITE_LOCKED ((__force req_flags_t)(1 << 19))
/* already slept for hybrid poll */
#define RQF_MQ_POLL_SLEPT ((__force req_flags_t)(1 << 20))
+/* ->timeout has been called, don't expire again */
+#define RQF_TIMED_OUT ((__force req_flags_t)(1 << 21))
/* flags that prevent us from merging requests: */
#define RQF_NOMERGE_FLAGS \
unsigned int dma_alignment;
struct blk_queue_tag *queue_tags;
- struct list_head tag_busy_list;
unsigned int nr_sorted;
unsigned int in_flight[2];
extern int blk_queue_init_tags(struct request_queue *, int, struct blk_queue_tag *, int);
extern void blk_queue_free_tags(struct request_queue *);
extern int blk_queue_resize_tags(struct request_queue *, int);
-extern void blk_queue_invalidate_tags(struct request_queue *);
extern struct blk_queue_tag *blk_init_tags(int, int);
extern void blk_free_tags(struct blk_queue_tag *);
CEPH_CAP_XATTR_SHARED)
#define CEPH_STAT_CAP_INLINE_DATA (CEPH_CAP_FILE_SHARED | \
CEPH_CAP_FILE_RD)
+#define CEPH_STAT_RSTAT CEPH_CAP_FILE_WREXTEND
#define CEPH_CAP_ANY_SHARED (CEPH_CAP_AUTH_SHARED | \
CEPH_CAP_LINK_SHARED | \
u64 r_tid; /* unique for this client */
struct rb_node r_node;
struct rb_node r_mc_node; /* map check */
+ struct work_struct r_complete_work;
struct ceph_osd *r_osd;
struct ceph_osd_request_target r_t;
struct timespec r_mtime; /* ditto */
u64 r_data_offset; /* ditto */
bool r_linger; /* don't resend on failure */
- bool r_abort_on_full; /* return ENOSPC when full */
/* internal */
unsigned long r_stamp; /* jiffies, send or check time */
struct rb_root linger_map_checks;
atomic_t num_requests;
atomic_t num_homeless;
+ bool abort_on_full; /* abort w/ ENOSPC when full */
+ int abort_err;
struct delayed_work timeout_work;
struct delayed_work osds_timeout_work;
#ifdef CONFIG_DEBUG_FS
struct ceph_msgpool msgpool_op_reply;
struct workqueue_struct *notify_wq;
+ struct workqueue_struct *completion_wq;
};
static inline bool ceph_osdmap_flag(struct ceph_osd_client *osdc, int flag)
extern void ceph_osdc_handle_map(struct ceph_osd_client *osdc,
struct ceph_msg *msg);
void ceph_osdc_update_epoch_barrier(struct ceph_osd_client *osdc, u32 eb);
+void ceph_osdc_abort_requests(struct ceph_osd_client *osdc, int err);
extern void osd_req_op_init(struct ceph_osd_request *osd_req,
unsigned int which, u16 opcode, u32 flags);
struct page **pages, u64 length,
u32 alignment, bool pages_from_pool,
bool own_pages);
-extern void osd_req_op_cls_init(struct ceph_osd_request *osd_req,
+extern int osd_req_op_cls_init(struct ceph_osd_request *osd_req,
unsigned int which, u16 opcode,
const char *class, const char *method);
extern int osd_req_op_xattr_init(struct ceph_osd_request *osd_req, unsigned int which,
const struct ceph_osds *new_acting,
bool any_change);
-int __ceph_object_locator_to_pg(struct ceph_pg_pool_info *pi,
- const struct ceph_object_id *oid,
- const struct ceph_object_locator *oloc,
- struct ceph_pg *raw_pgid);
+void __ceph_object_locator_to_pg(struct ceph_pg_pool_info *pi,
+ const struct ceph_object_id *oid,
+ const struct ceph_object_locator *oloc,
+ struct ceph_pg *raw_pgid);
int ceph_object_locator_to_pg(struct ceph_osdmap *osdmap,
const struct ceph_object_id *oid,
const struct ceph_object_locator *oloc,
/* SPDX-License-Identifier: GPL-2.0 */
/*
- * See Documentation/circular-buffers.txt for more information.
+ * See Documentation/core-api/circular-buffers.rst for more information.
*/
#ifndef _LINUX_CIRC_BUF_H
kuid_t ia_uid;
kgid_t ia_gid;
loff_t ia_size;
- struct timespec ia_atime;
- struct timespec ia_mtime;
- struct timespec ia_ctime;
+ struct timespec64 ia_atime;
+ struct timespec64 ia_mtime;
+ struct timespec64 ia_ctime;
/*
* Not an attribute, but an auxiliary info for filesystems wanting to
};
dev_t i_rdev;
loff_t i_size;
- struct timespec i_atime;
- struct timespec i_mtime;
- struct timespec i_ctime;
+ struct timespec64 i_atime;
+ struct timespec64 i_mtime;
+ struct timespec64 i_ctime;
spinlock_t i_lock; /* i_blocks, i_bytes, maybe i_size */
unsigned short i_bytes;
unsigned int i_blkbits;
extern int vfs_cancel_lock(struct file *filp, struct file_lock *fl);
extern int locks_lock_inode_wait(struct inode *inode, struct file_lock *fl);
extern int __break_lease(struct inode *inode, unsigned int flags, unsigned int type);
-extern void lease_get_mtime(struct inode *, struct timespec *time);
+extern void lease_get_mtime(struct inode *, struct timespec64 *time);
extern int generic_setlease(struct file *, long, struct file_lock **, void **priv);
extern int vfs_setlease(struct file *, long, struct file_lock **, void **);
extern int lease_modify(struct file_lock *, int, struct list_head *);
return 0;
}
-static inline void lease_get_mtime(struct inode *inode, struct timespec *time)
+static inline void lease_get_mtime(struct inode *inode,
+ struct timespec64 *time)
{
return;
}
inode->i_gid = make_kgid(inode->i_sb->s_user_ns, gid);
}
-extern struct timespec current_time(struct inode *inode);
+extern struct timespec64 timespec64_trunc(struct timespec64 t, unsigned gran);
+extern struct timespec64 current_time(struct inode *inode);
/*
* Snapshotting support.
ssize_t (*listxattr) (struct dentry *, char *, size_t);
int (*fiemap)(struct inode *, struct fiemap_extent_info *, u64 start,
u64 len);
- int (*update_time)(struct inode *, struct timespec *, int);
+ int (*update_time)(struct inode *, struct timespec64 *, int);
int (*atomic_open)(struct inode *, struct dentry *,
struct file *, unsigned open_flag,
umode_t create_mode, int *opened);
extern void ihold(struct inode * inode);
extern void iput(struct inode *);
-extern int generic_update_time(struct inode *, struct timespec *, int);
+extern int generic_update_time(struct inode *, struct timespec64 *, int);
/* /sys/fs */
extern struct kobject *fs_kobj;
struct fsnotify_ops {
int (*handle_event)(struct fsnotify_group *group,
struct inode *inode,
- struct fsnotify_mark *inode_mark,
- struct fsnotify_mark *vfsmount_mark,
u32 mask, const void *data, int data_type,
const unsigned char *file_name, u32 cookie,
struct fsnotify_iter_info *iter_info);
#define FSNOTIFY_EVENT_PATH 1
#define FSNOTIFY_EVENT_INODE 2
+enum fsnotify_obj_type {
+ FSNOTIFY_OBJ_TYPE_INODE,
+ FSNOTIFY_OBJ_TYPE_VFSMOUNT,
+ FSNOTIFY_OBJ_TYPE_COUNT,
+ FSNOTIFY_OBJ_TYPE_DETACHED = FSNOTIFY_OBJ_TYPE_COUNT
+};
+
+#define FSNOTIFY_OBJ_TYPE_INODE_FL (1U << FSNOTIFY_OBJ_TYPE_INODE)
+#define FSNOTIFY_OBJ_TYPE_VFSMOUNT_FL (1U << FSNOTIFY_OBJ_TYPE_VFSMOUNT)
+#define FSNOTIFY_OBJ_ALL_TYPES_MASK ((1U << FSNOTIFY_OBJ_TYPE_COUNT) - 1)
+
+struct fsnotify_iter_info {
+ struct fsnotify_mark *marks[FSNOTIFY_OBJ_TYPE_COUNT];
+ unsigned int report_mask;
+ int srcu_idx;
+};
+
+static inline bool fsnotify_iter_should_report_type(
+ struct fsnotify_iter_info *iter_info, int type)
+{
+ return (iter_info->report_mask & (1U << type));
+}
+
+static inline void fsnotify_iter_set_report_type(
+ struct fsnotify_iter_info *iter_info, int type)
+{
+ iter_info->report_mask |= (1U << type);
+}
+
+static inline void fsnotify_iter_set_report_type_mark(
+ struct fsnotify_iter_info *iter_info, int type,
+ struct fsnotify_mark *mark)
+{
+ iter_info->marks[type] = mark;
+ iter_info->report_mask |= (1U << type);
+}
+
+#define FSNOTIFY_ITER_FUNCS(name, NAME) \
+static inline struct fsnotify_mark *fsnotify_iter_##name##_mark( \
+ struct fsnotify_iter_info *iter_info) \
+{ \
+ return (iter_info->report_mask & FSNOTIFY_OBJ_TYPE_##NAME##_FL) ? \
+ iter_info->marks[FSNOTIFY_OBJ_TYPE_##NAME] : NULL; \
+}
+
+FSNOTIFY_ITER_FUNCS(inode, INODE)
+FSNOTIFY_ITER_FUNCS(vfsmount, VFSMOUNT)
+
+#define fsnotify_foreach_obj_type(type) \
+ for (type = 0; type < FSNOTIFY_OBJ_TYPE_COUNT; type++)
+
/*
* Inode / vfsmount point to this structure which tracks all marks attached to
* the inode / vfsmount. The reference to inode / vfsmount is held by this
*/
struct fsnotify_mark_connector {
spinlock_t lock;
-#define FSNOTIFY_OBJ_TYPE_INODE 0x01
-#define FSNOTIFY_OBJ_TYPE_VFSMOUNT 0x02
-#define FSNOTIFY_OBJ_ALL_TYPES (FSNOTIFY_OBJ_TYPE_INODE | \
- FSNOTIFY_OBJ_TYPE_VFSMOUNT)
- unsigned int flags; /* Type of object [lock] */
+ unsigned int type; /* Type of object [lock] */
union { /* Object pointer [lock] */
struct inode *inode;
struct vfsmount *mnt;
extern int fsnotify_add_mark(struct fsnotify_mark *mark, struct inode *inode,
struct vfsmount *mnt, int allow_dups);
extern int fsnotify_add_mark_locked(struct fsnotify_mark *mark,
- struct inode *inode, struct vfsmount *mnt, int allow_dups);
+ struct inode *inode, struct vfsmount *mnt,
+ int allow_dups);
+/* attach the mark to the inode */
+static inline int fsnotify_add_inode_mark(struct fsnotify_mark *mark,
+ struct inode *inode,
+ int allow_dups)
+{
+ return fsnotify_add_mark(mark, inode, NULL, allow_dups);
+}
+static inline int fsnotify_add_inode_mark_locked(struct fsnotify_mark *mark,
+ struct inode *inode,
+ int allow_dups)
+{
+ return fsnotify_add_mark_locked(mark, inode, NULL, allow_dups);
+}
/* given a group and a mark, flag mark to be freed when all references are dropped */
extern void fsnotify_destroy_mark(struct fsnotify_mark *mark,
struct fsnotify_group *group);
/* run all the marks in a group, and clear all of the vfsmount marks */
static inline void fsnotify_clear_vfsmount_marks_by_group(struct fsnotify_group *group)
{
- fsnotify_clear_marks_by_group(group, FSNOTIFY_OBJ_TYPE_VFSMOUNT);
+ fsnotify_clear_marks_by_group(group, FSNOTIFY_OBJ_TYPE_VFSMOUNT_FL);
}
/* run all the marks in a group, and clear all of the inode marks */
static inline void fsnotify_clear_inode_marks_by_group(struct fsnotify_group *group)
{
- fsnotify_clear_marks_by_group(group, FSNOTIFY_OBJ_TYPE_INODE);
+ fsnotify_clear_marks_by_group(group, FSNOTIFY_OBJ_TYPE_INODE_FL);
}
extern void fsnotify_get_mark(struct fsnotify_mark *mark);
extern void fsnotify_put_mark(struct fsnotify_mark *mark);
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Ftrace header. For implementation details beyond the random comments
- * scattered below, see: Documentation/trace/ftrace-design.txt
+ * scattered below, see: Documentation/trace/ftrace-design.rst
*/
#ifndef _LINUX_FTRACE_H
+++ /dev/null
-/*
- * i2c-gpio interface to platform code
- *
- * Copyright (C) 2007 Atmel Corporation
- *
- * 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_I2C_GPIO_H
-#define _LINUX_I2C_GPIO_H
-
-/**
- * struct i2c_gpio_platform_data - Platform-dependent data for i2c-gpio
- * @udelay: signal toggle delay. SCL frequency is (500 / udelay) kHz
- * @timeout: clock stretching timeout in jiffies. If the slave keeps
- * SCL low for longer than this, the transfer will time out.
- * @sda_is_open_drain: SDA is configured as open drain, i.e. the pin
- * isn't actively driven high when setting the output value high.
- * gpio_get_value() must return the actual pin state even if the
- * pin is configured as an output.
- * @scl_is_open_drain: SCL is set up as open drain. Same requirements
- * as for sda_is_open_drain apply.
- * @scl_is_output_only: SCL output drivers cannot be turned off.
- */
-struct i2c_gpio_platform_data {
- int udelay;
- int timeout;
- unsigned int sda_is_open_drain:1;
- unsigned int scl_is_open_drain:1;
- unsigned int scl_is_output_only:1;
-};
-
-#endif /* _LINUX_I2C_GPIO_H */
+++ /dev/null
-/*
- * i2c-mux-gpio interface to platform code
- *
- * Peter Korsgaard <peter.korsgaard@barco.com>
- *
- * 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_I2C_MUX_GPIO_H
-#define _LINUX_I2C_MUX_GPIO_H
-
-/* MUX has no specific idle mode */
-#define I2C_MUX_GPIO_NO_IDLE ((unsigned)-1)
-
-/**
- * struct i2c_mux_gpio_platform_data - Platform-dependent data for i2c-mux-gpio
- * @parent: Parent I2C bus adapter number
- * @base_nr: Base I2C bus number to number adapters from or zero for dynamic
- * @values: Array of bitmasks of GPIO settings (low/high) for each
- * position
- * @n_values: Number of multiplexer positions (busses to instantiate)
- * @classes: Optional I2C auto-detection classes
- * @gpio_chip: Optional GPIO chip name; if set, GPIO pin numbers are given
- * relative to the base GPIO number of that chip
- * @gpios: Array of GPIO numbers used to control MUX
- * @n_gpios: Number of GPIOs used to control MUX
- * @idle: Bitmask to write to MUX when idle or GPIO_I2CMUX_NO_IDLE if not used
- */
-struct i2c_mux_gpio_platform_data {
- int parent;
- int base_nr;
- const unsigned *values;
- int n_values;
- const unsigned *classes;
- char *gpio_chip;
- const unsigned *gpios;
- int n_gpios;
- unsigned idle;
-};
-
-#endif /* _LINUX_I2C_MUX_GPIO_H */
+++ /dev/null
-/*
- * i2c-ocores.h - definitions for the i2c-ocores interface
- *
- * Peter Korsgaard <jacmet@sunsite.dk>
- *
- * This file is licensed under the terms of the GNU General Public License
- * version 2. This program is licensed "as is" without any warranty of any
- * kind, whether express or implied.
- */
-
-#ifndef _LINUX_I2C_OCORES_H
-#define _LINUX_I2C_OCORES_H
-
-struct ocores_i2c_platform_data {
- u32 reg_shift; /* register offset shift value */
- u32 reg_io_width; /* register io read/write width */
- u32 clock_khz; /* input clock in kHz */
- bool big_endian; /* registers are big endian */
- u8 num_devices; /* number of devices in the devices list */
- struct i2c_board_info const *devices; /* devices connected to the bus */
-};
-
-#endif /* _LINUX_I2C_OCORES_H */
+++ /dev/null
-/* SPDX-License-Identifier: GPL-2.0 */
-#ifndef __I2C_OMAP_H__
-#define __I2C_OMAP_H__
-
-#include <linux/platform_device.h>
-
-/*
- * Version 2 of the I2C peripheral unit has a different register
- * layout and extra registers. The ID register in the V2 peripheral
- * unit on the OMAP4430 reports the same ID as the V1 peripheral
- * unit on the OMAP3530, so we must inform the driver which IP
- * version we know it is running on from platform / cpu-specific
- * code using these constants in the hwmod class definition.
- */
-
-#define OMAP_I2C_IP_VERSION_1 1
-#define OMAP_I2C_IP_VERSION_2 2
-
-/* struct omap_i2c_bus_platform_data .flags meanings */
-
-#define OMAP_I2C_FLAG_NO_FIFO BIT(0)
-#define OMAP_I2C_FLAG_SIMPLE_CLOCK BIT(1)
-#define OMAP_I2C_FLAG_16BIT_DATA_REG BIT(2)
-#define OMAP_I2C_FLAG_ALWAYS_ARMXOR_CLK BIT(5)
-#define OMAP_I2C_FLAG_FORCE_19200_INT_CLK BIT(6)
-/* how the CPU address bus must be translated for I2C unit access */
-#define OMAP_I2C_FLAG_BUS_SHIFT_NONE 0
-#define OMAP_I2C_FLAG_BUS_SHIFT_1 BIT(7)
-#define OMAP_I2C_FLAG_BUS_SHIFT_2 BIT(8)
-#define OMAP_I2C_FLAG_BUS_SHIFT__SHIFT 7
-
-struct omap_i2c_bus_platform_data {
- u32 clkrate;
- u32 rev;
- u32 flags;
- void (*set_mpu_wkup_lat)(struct device *dev, long set);
-};
-
-#endif
+++ /dev/null
-/* SPDX-License-Identifier: GPL-2.0 */
-#ifndef I2C_PCA9564_PLATFORM_H
-#define I2C_PCA9564_PLATFORM_H
-
-struct i2c_pca9564_pf_platform_data {
- int i2c_clock_speed; /* values are defined in linux/i2c-algo-pca.h */
- int timeout; /* timeout in jiffies */
-};
-
-#endif /* I2C_PCA9564_PLATFORM_H */
+++ /dev/null
-/*
- * Header file for I2C support on PNX010x/4008.
- *
- * Author: Dennis Kovalev <dkovalev@ru.mvista.com>
- *
- * 2004-2006 (c) MontaVista Software, Inc. This file is licensed under
- * the terms of the GNU General Public License version 2. This program
- * is licensed "as is" without any warranty of any kind, whether express
- * or implied.
- */
-
-#ifndef __I2C_PNX_H__
-#define __I2C_PNX_H__
-
-struct platform_device;
-struct clk;
-
-struct i2c_pnx_mif {
- int ret; /* Return value */
- int mode; /* Interface mode */
- struct completion complete; /* I/O completion */
- struct timer_list timer; /* Timeout */
- u8 * buf; /* Data buffer */
- int len; /* Length of data buffer */
- int order; /* RX Bytes to order via TX */
-};
-
-struct i2c_pnx_algo_data {
- void __iomem *ioaddr;
- struct i2c_pnx_mif mif;
- int last;
- struct clk *clk;
- struct i2c_adapter adapter;
- int irq;
- u32 timeout;
-};
-
-#endif /* __I2C_PNX_H__ */
+++ /dev/null
-/*
- * i2c-xiic.h
- * Copyright (c) 2009 Intel Corporation
- *
- * 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-/* Supports:
- * Xilinx IIC
- */
-
-#ifndef _LINUX_I2C_XIIC_H
-#define _LINUX_I2C_XIIC_H
-
-/**
- * struct xiic_i2c_platform_data - Platform data of the Xilinx I2C driver
- * @num_devices: Number of devices that shall be added when the driver
- * is probed.
- * @devices: The actuall devices to add.
- *
- * This purpose of this platform data struct is to be able to provide a number
- * of devices that should be added to the I2C bus. The reason is that sometimes
- * the I2C board info is not enough, a new PCI board can for instance be
- * plugged into a standard PC, and the bus number might be unknown at
- * early init time.
- */
-struct xiic_i2c_platform_data {
- u8 num_devices;
- struct i2c_board_info const *devices;
-};
-
-#endif /* _LINUX_I2C_XIIC_H */
* @addr: stored in i2c_client.addr
* @dev_name: Overrides the default <busnr>-<addr> dev_name if set
* @platform_data: stored in i2c_client.dev.platform_data
- * @archdata: copied into i2c_client.dev.archdata
* @of_node: pointer to OpenFirmware device node
* @fwnode: device node supplied by the platform firmware
* @properties: additional device properties for the device
unsigned short addr;
const char *dev_name;
void *platform_data;
- struct dev_archdata *archdata;
struct device_node *of_node;
struct fwnode_handle *fwnode;
const struct property_entry *properties;
*i2c_of_match_device(const struct of_device_id *matches,
struct i2c_client *client);
+int of_i2c_get_board_info(struct device *dev, struct device_node *node,
+ struct i2c_board_info *info);
+
#else
static inline struct i2c_client *of_find_i2c_device_by_node(struct device_node *node)
return NULL;
}
+static inline int of_i2c_get_board_info(struct device *dev,
+ struct device_node *node,
+ struct i2c_board_info *info)
+{
+ return -ENOTSUPP;
+}
+
#endif /* CONFIG_OF */
#if IS_ENABLED(CONFIG_ACPI)
KCOV_MODE_TRACE_CMP = 3,
};
+#define KCOV_IN_CTXSW (1 << 30)
+
void kcov_task_init(struct task_struct *t);
void kcov_task_exit(struct task_struct *t);
+#define kcov_prepare_switch(t) \
+do { \
+ (t)->kcov_mode |= KCOV_IN_CTXSW; \
+} while (0)
+
+#define kcov_finish_switch(t) \
+do { \
+ (t)->kcov_mode &= ~KCOV_IN_CTXSW; \
+} while (0)
+
#else
static inline void kcov_task_init(struct task_struct *t) {}
static inline void kcov_task_exit(struct task_struct *t) {}
+static inline void kcov_prepare_switch(struct task_struct *t) {}
+static inline void kcov_finish_switch(struct task_struct *t) {}
#endif /* CONFIG_KCOV */
#endif /* _LINUX_KCOV_H */
MEMCG_HIGH,
MEMCG_MAX,
MEMCG_OOM,
+ MEMCG_OOM_KILL,
MEMCG_SWAP_MAX,
MEMCG_SWAP_FAIL,
MEMCG_NR_MEMORY_EVENTS,
rcu_read_lock();
memcg = mem_cgroup_from_task(rcu_dereference(mm->owner));
- if (likely(memcg)) {
+ if (likely(memcg))
count_memcg_events(memcg, idx, 1);
- if (idx == OOM_KILL)
- cgroup_file_notify(&memcg->events_file);
- }
rcu_read_unlock();
}
cgroup_file_notify(&memcg->events_file);
}
+static inline void memcg_memory_event_mm(struct mm_struct *mm,
+ enum memcg_memory_event event)
+{
+ struct mem_cgroup *memcg;
+
+ if (mem_cgroup_disabled())
+ return;
+
+ rcu_read_lock();
+ memcg = mem_cgroup_from_task(rcu_dereference(mm->owner));
+ if (likely(memcg))
+ memcg_memory_event(memcg, event);
+ rcu_read_unlock();
+}
+
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
void mem_cgroup_split_huge_fixup(struct page *head);
#endif
{
}
+static inline void memcg_memory_event_mm(struct mm_struct *mm,
+ enum memcg_memory_event event)
+{
+}
+
static inline enum mem_cgroup_protection mem_cgroup_protected(
struct mem_cgroup *root, struct mem_cgroup *memcg)
{
DMI_PRODUCT_VERSION,
DMI_PRODUCT_SERIAL,
DMI_PRODUCT_UUID,
+ DMI_PRODUCT_SKU,
DMI_PRODUCT_FAMILY,
DMI_BOARD_VENDOR,
DMI_BOARD_NAME,
extern struct dentry *kern_path_locked(const char *, struct path *);
extern int kern_path_mountpoint(int, const char *, struct path *, unsigned int);
+extern struct dentry *try_lookup_one_len(const char *, struct dentry *, int);
extern struct dentry *lookup_one_len(const char *, struct dentry *, int);
extern struct dentry *lookup_one_len_unlocked(const char *, struct dentry *, int);
rcu_read_lock();
nat_hook = rcu_dereference(nf_nat_hook);
- if (nat_hook->decode_session)
+ if (nat_hook && nat_hook->decode_session)
nat_hook->decode_session(skb, fl);
rcu_read_unlock();
#endif
/* Set is defined with timeout support: timeout value may be 0 */
#define IPSET_NO_TIMEOUT UINT_MAX
+/* Max timeout value, see msecs_to_jiffies() in jiffies.h */
+#define IPSET_MAX_TIMEOUT (UINT_MAX >> 1)/MSEC_PER_SEC
+
#define ip_set_adt_opt_timeout(opt, set) \
((opt)->ext.timeout != IPSET_NO_TIMEOUT ? (opt)->ext.timeout : (set)->timeout)
unsigned int timeout = ip_set_get_h32(tb);
/* Normalize to fit into jiffies */
- if (timeout > UINT_MAX/MSEC_PER_SEC)
- timeout = UINT_MAX/MSEC_PER_SEC;
+ if (timeout > IPSET_MAX_TIMEOUT)
+ timeout = IPSET_MAX_TIMEOUT;
- /* Userspace supplied TIMEOUT parameter: adjust crazy size */
- return timeout == IPSET_NO_TIMEOUT ? IPSET_NO_TIMEOUT - 1 : timeout;
+ return timeout;
}
static inline bool
static inline u32
ip_set_timeout_get(const unsigned long *timeout)
{
- return *timeout == IPSET_ELEM_PERMANENT ? 0 :
- jiffies_to_msecs(*timeout - jiffies)/MSEC_PER_SEC;
+ u32 t;
+
+ if (*timeout == IPSET_ELEM_PERMANENT)
+ return 0;
+
+ t = jiffies_to_msecs(*timeout - jiffies)/MSEC_PER_SEC;
+ /* Zero value in userspace means no timeout */
+ return t == 0 ? 1 : t;
}
#endif /* __KERNEL__ */
--- /dev/null
+/*
+ * i2c-gpio interface to platform code
+ *
+ * Copyright (C) 2007 Atmel Corporation
+ *
+ * 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_I2C_GPIO_H
+#define _LINUX_I2C_GPIO_H
+
+/**
+ * struct i2c_gpio_platform_data - Platform-dependent data for i2c-gpio
+ * @udelay: signal toggle delay. SCL frequency is (500 / udelay) kHz
+ * @timeout: clock stretching timeout in jiffies. If the slave keeps
+ * SCL low for longer than this, the transfer will time out.
+ * @sda_is_open_drain: SDA is configured as open drain, i.e. the pin
+ * isn't actively driven high when setting the output value high.
+ * gpio_get_value() must return the actual pin state even if the
+ * pin is configured as an output.
+ * @scl_is_open_drain: SCL is set up as open drain. Same requirements
+ * as for sda_is_open_drain apply.
+ * @scl_is_output_only: SCL output drivers cannot be turned off.
+ */
+struct i2c_gpio_platform_data {
+ int udelay;
+ int timeout;
+ unsigned int sda_is_open_drain:1;
+ unsigned int scl_is_open_drain:1;
+ unsigned int scl_is_output_only:1;
+};
+
+#endif /* _LINUX_I2C_GPIO_H */
--- /dev/null
+/*
+ * i2c-mux-gpio interface to platform code
+ *
+ * Peter Korsgaard <peter.korsgaard@barco.com>
+ *
+ * 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_I2C_MUX_GPIO_H
+#define _LINUX_I2C_MUX_GPIO_H
+
+/* MUX has no specific idle mode */
+#define I2C_MUX_GPIO_NO_IDLE ((unsigned)-1)
+
+/**
+ * struct i2c_mux_gpio_platform_data - Platform-dependent data for i2c-mux-gpio
+ * @parent: Parent I2C bus adapter number
+ * @base_nr: Base I2C bus number to number adapters from or zero for dynamic
+ * @values: Array of bitmasks of GPIO settings (low/high) for each
+ * position
+ * @n_values: Number of multiplexer positions (busses to instantiate)
+ * @classes: Optional I2C auto-detection classes
+ * @gpio_chip: Optional GPIO chip name; if set, GPIO pin numbers are given
+ * relative to the base GPIO number of that chip
+ * @gpios: Array of GPIO numbers used to control MUX
+ * @n_gpios: Number of GPIOs used to control MUX
+ * @idle: Bitmask to write to MUX when idle or GPIO_I2CMUX_NO_IDLE if not used
+ */
+struct i2c_mux_gpio_platform_data {
+ int parent;
+ int base_nr;
+ const unsigned *values;
+ int n_values;
+ const unsigned *classes;
+ char *gpio_chip;
+ const unsigned *gpios;
+ int n_gpios;
+ unsigned idle;
+};
+
+#endif /* _LINUX_I2C_MUX_GPIO_H */
--- /dev/null
+/*
+ * i2c-ocores.h - definitions for the i2c-ocores interface
+ *
+ * Peter Korsgaard <jacmet@sunsite.dk>
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#ifndef _LINUX_I2C_OCORES_H
+#define _LINUX_I2C_OCORES_H
+
+struct ocores_i2c_platform_data {
+ u32 reg_shift; /* register offset shift value */
+ u32 reg_io_width; /* register io read/write width */
+ u32 clock_khz; /* input clock in kHz */
+ bool big_endian; /* registers are big endian */
+ u8 num_devices; /* number of devices in the devices list */
+ struct i2c_board_info const *devices; /* devices connected to the bus */
+};
+
+#endif /* _LINUX_I2C_OCORES_H */
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __I2C_OMAP_H__
+#define __I2C_OMAP_H__
+
+#include <linux/platform_device.h>
+
+/*
+ * Version 2 of the I2C peripheral unit has a different register
+ * layout and extra registers. The ID register in the V2 peripheral
+ * unit on the OMAP4430 reports the same ID as the V1 peripheral
+ * unit on the OMAP3530, so we must inform the driver which IP
+ * version we know it is running on from platform / cpu-specific
+ * code using these constants in the hwmod class definition.
+ */
+
+#define OMAP_I2C_IP_VERSION_1 1
+#define OMAP_I2C_IP_VERSION_2 2
+
+/* struct omap_i2c_bus_platform_data .flags meanings */
+
+#define OMAP_I2C_FLAG_NO_FIFO BIT(0)
+#define OMAP_I2C_FLAG_SIMPLE_CLOCK BIT(1)
+#define OMAP_I2C_FLAG_16BIT_DATA_REG BIT(2)
+#define OMAP_I2C_FLAG_ALWAYS_ARMXOR_CLK BIT(5)
+#define OMAP_I2C_FLAG_FORCE_19200_INT_CLK BIT(6)
+/* how the CPU address bus must be translated for I2C unit access */
+#define OMAP_I2C_FLAG_BUS_SHIFT_NONE 0
+#define OMAP_I2C_FLAG_BUS_SHIFT_1 BIT(7)
+#define OMAP_I2C_FLAG_BUS_SHIFT_2 BIT(8)
+#define OMAP_I2C_FLAG_BUS_SHIFT__SHIFT 7
+
+struct omap_i2c_bus_platform_data {
+ u32 clkrate;
+ u32 rev;
+ u32 flags;
+ void (*set_mpu_wkup_lat)(struct device *dev, long set);
+};
+
+#endif
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef I2C_PCA9564_PLATFORM_H
+#define I2C_PCA9564_PLATFORM_H
+
+struct i2c_pca9564_pf_platform_data {
+ int i2c_clock_speed; /* values are defined in linux/i2c-algo-pca.h */
+ int timeout; /* timeout in jiffies */
+};
+
+#endif /* I2C_PCA9564_PLATFORM_H */
--- /dev/null
+/*
+ * i2c-xiic.h
+ * Copyright (c) 2009 Intel Corporation
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* Supports:
+ * Xilinx IIC
+ */
+
+#ifndef _LINUX_I2C_XIIC_H
+#define _LINUX_I2C_XIIC_H
+
+/**
+ * struct xiic_i2c_platform_data - Platform data of the Xilinx I2C driver
+ * @num_devices: Number of devices that shall be added when the driver
+ * is probed.
+ * @devices: The actuall devices to add.
+ *
+ * This purpose of this platform data struct is to be able to provide a number
+ * of devices that should be added to the I2C bus. The reason is that sometimes
+ * the I2C board info is not enough, a new PCI board can for instance be
+ * plugged into a standard PC, and the bus number might be unknown at
+ * early init time.
+ */
+struct xiic_i2c_platform_data {
+ u8 num_devices;
+ struct i2c_board_info const *devices;
+};
+
+#endif /* _LINUX_I2C_XIIC_H */
* struct mlxreg_core_data - attributes control data:
*
* @label: attribute label;
- * @label: attribute register offset;
* @reg: attribute register;
* @mask: attribute access mask;
- * @mode: access mode;
* @bit: attribute effective bit;
+ * @mode: access mode;
* @np - pointer to node platform associated with attribute;
* @hpdev - hotplug device data;
* @health_cntr: dynamic device health indication counter;
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
- * For further information, see the Documentation/spi/sc18is602 file.
+ * For further information, see the Documentation/spi/spi-sc18is602 file.
*/
/**
#include <drm/drm_mode.h>
-struct sh_mobile_meram_cfg;
-struct sh_mobile_meram_info;
-
enum shmob_drm_clk_source {
SHMOB_DRM_CLK_BUS,
SHMOB_DRM_CLK_PERIPHERAL,
struct shmob_drm_interface_data iface;
struct shmob_drm_panel_data panel;
struct shmob_drm_backlight_data backlight;
- const struct sh_mobile_meram_cfg *meram;
};
#endif /* __SHMOB_DRM_H__ */
#ifdef CONFIG_PROC_FS
+typedef int (*proc_write_t)(struct file *, char *, size_t);
+
extern void proc_root_init(void);
extern void proc_flush_task(struct task_struct *);
struct proc_dir_entry *proc_create_net_single(const char *name, umode_t mode,
struct proc_dir_entry *parent,
int (*show)(struct seq_file *, void *), void *data);
+struct proc_dir_entry *proc_create_net_data_write(const char *name, umode_t mode,
+ struct proc_dir_entry *parent,
+ const struct seq_operations *ops,
+ proc_write_t write,
+ unsigned int state_size, void *data);
+struct proc_dir_entry *proc_create_net_single_write(const char *name, umode_t mode,
+ struct proc_dir_entry *parent,
+ int (*show)(struct seq_file *, void *),
+ proc_write_t write,
+ void *data);
#else /* CONFIG_PROC_FS */
struct pstore_info *psi;
enum pstore_type_id type;
u64 id;
- struct timespec time;
+ struct timespec64 time;
char *buf;
ssize_t size;
ssize_t ecc_notice_size;
*
* The barrier() is needed to make sure compiler doesn't cache first element [1],
* as this loop can be restarted [2]
- * [1] Documentation/atomic_ops.txt around line 114
+ * [1] Documentation/core-api/atomic_ops.rst around line 114
* [2] Documentation/RCU/rculist_nulls.txt around line 146
*/
#define hlist_nulls_for_each_entry_rcu(tpos, pos, head, member) \
pid_t pid;
pid_t tgid;
-#ifdef CONFIG_CC_STACKPROTECTOR
+#ifdef CONFIG_STACKPROTECTOR
/* Canary value for the -fstack-protector GCC feature: */
unsigned long stack_canary;
#endif
#ifdef CONFIG_KCOV
/* Coverage collection mode enabled for this task (0 if disabled): */
- enum kcov_mode kcov_mode;
+ unsigned int kcov_mode;
/* Size of the kcov_area: */
unsigned int kcov_size;
struct memcg_cache_array __rcu *memcg_caches;
struct list_head __root_caches_node;
struct list_head children;
+ bool dying;
};
struct {
struct mem_cgroup *memcg;
#include <linux/sched.h>
#include <linux/random.h>
-#ifdef CONFIG_CC_STACKPROTECTOR
+#ifdef CONFIG_STACKPROTECTOR
# include <asm/stackprotector.h>
#else
static inline void boot_init_stack_canary(void)
kuid_t uid;
kgid_t gid;
loff_t size;
- struct timespec atime;
- struct timespec mtime;
- struct timespec ctime;
- struct timespec btime; /* File creation time */
+ struct timespec64 atime;
+ struct timespec64 mtime;
+ struct timespec64 ctime;
+ struct timespec64 btime; /* File creation time */
u64 blocks;
};
#include <linux/types.h>
struct file;
+struct task_struct;
/* Descriptions of the types of units to
* print in */
/*
* Kernel Tracepoint API.
*
- * See Documentation/trace/tracepoints.txt.
+ * See Documentation/trace/tracepoints.rst.
*
* Copyright (C) 2008-2014 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
*
if (weak_barriers)
virt_rmb();
else
- rmb();
+ dma_rmb();
}
static inline void virtio_wmb(bool weak_barriers)
if (weak_barriers)
virt_wmb();
else
- wmb();
+ dma_wmb();
}
static inline void virtio_store_mb(bool weak_barriers,
/* alternate persistence engine */
struct ip_vs_pe __rcu *pe;
+ int conntrack_afmask;
struct rcu_head rcu_head;
};
return false;
}
+static inline int ip_vs_register_conntrack(struct ip_vs_service *svc)
+{
+#if IS_ENABLED(CONFIG_NF_CONNTRACK)
+ int afmask = (svc->af == AF_INET6) ? 2 : 1;
+ int ret = 0;
+
+ if (!(svc->conntrack_afmask & afmask)) {
+ ret = nf_ct_netns_get(svc->ipvs->net, svc->af);
+ if (ret >= 0)
+ svc->conntrack_afmask |= afmask;
+ }
+ return ret;
+#else
+ return 0;
+#endif
+}
+
+static inline void ip_vs_unregister_conntrack(struct ip_vs_service *svc)
+{
+#if IS_ENABLED(CONFIG_NF_CONNTRACK)
+ int afmask = (svc->af == AF_INET6) ? 2 : 1;
+
+ if (svc->conntrack_afmask & afmask) {
+ nf_ct_netns_put(svc->ipvs->net, svc->af);
+ svc->conntrack_afmask &= ~afmask;
+ }
+#endif
+}
+
static inline int
ip_vs_dest_conn_overhead(struct ip_vs_dest *dest)
{
bool *addit);
bool nf_conncount_add(struct hlist_head *head,
- const struct nf_conntrack_tuple *tuple);
+ const struct nf_conntrack_tuple *tuple,
+ const struct nf_conntrack_zone *zone);
void nf_conncount_cache_free(struct hlist_head *hhead);
+++ /dev/null
-/* SPDX-License-Identifier: GPL-2.0 */
-#ifndef _NFT_DUP_H_
-#define _NFT_DUP_H_
-
-struct nft_dup_inet {
- enum nft_registers sreg_addr:8;
- enum nft_registers sreg_dev:8;
-};
-
-#endif /* _NFT_DUP_H_ */
};
#define SCTP_INPUT_CB(__skb) ((struct sctp_input_cb *)&((__skb)->cb[0]))
+struct sctp_output_cb {
+ struct sk_buff *last;
+};
+#define SCTP_OUTPUT_CB(__skb) ((struct sctp_output_cb *)&((__skb)->cb[0]))
+
static inline const struct sk_buff *sctp_gso_headskb(const struct sk_buff *skb)
{
const struct sctp_chunk *chunk = SCTP_INPUT_CB(skb)->chunk;
struct strparser strp;
void (*saved_data_ready)(struct sock *sk);
- unsigned int (*sk_poll)(struct file *file, struct socket *sock,
- struct poll_table_struct *wait);
+ __poll_t (*sk_poll_mask)(struct socket *sock, __poll_t events);
struct sk_buff *recv_pkt;
u8 control;
bool decrypted;
void tls_sw_free_resources_rx(struct sock *sk);
int tls_sw_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
int nonblock, int flags, int *addr_len);
-unsigned int tls_sw_poll(struct file *file, struct socket *sock,
- struct poll_table_struct *wait);
+__poll_t tls_sw_poll_mask(struct socket *sock, __poll_t events);
ssize_t tls_sw_splice_read(struct socket *sock, loff_t *ppos,
struct pipe_inode_info *pipe,
size_t len, unsigned int flags);
#undef IFLITTLE
struct __aio_sigset {
- sigset_t __user *sigmask;
+ const sigset_t __user *sigmask;
size_t sigsetsize;
};
IPS_EXPECTED | IPS_CONFIRMED | IPS_DYING |
IPS_SEQ_ADJUST | IPS_TEMPLATE | IPS_OFFLOAD),
- __IPS_MAX_BIT = 14,
+ __IPS_MAX_BIT = 15,
};
/* Connection tracking event types */
* @NFT_SET_INTERVAL: set contains intervals
* @NFT_SET_MAP: set is used as a dictionary
* @NFT_SET_TIMEOUT: set uses timeouts
- * @NFT_SET_EVAL: set contains expressions for evaluation
+ * @NFT_SET_EVAL: set can be updated from the evaluation path
* @NFT_SET_OBJECT: set contains stateful objects
*/
enum nft_set_flags {
* only the %NL80211_ATTR_IE data is used and updated with this command.
*
* @NL80211_CMD_SET_PMK: For offloaded 4-Way handshake, set the PMK or PMK-R0
- * for the given authenticator address (specified with &NL80211_ATTR_MAC).
- * When &NL80211_ATTR_PMKR0_NAME is set, &NL80211_ATTR_PMK specifies the
+ * for the given authenticator address (specified with %NL80211_ATTR_MAC).
+ * When %NL80211_ATTR_PMKR0_NAME is set, %NL80211_ATTR_PMK specifies the
* PMK-R0, otherwise it specifies the PMK.
* @NL80211_CMD_DEL_PMK: For offloaded 4-Way handshake, delete the previously
* configured PMK for the authenticator address identified by
- * &NL80211_ATTR_MAC.
+ * %NL80211_ATTR_MAC.
* @NL80211_CMD_PORT_AUTHORIZED: An event that indicates that the 4 way
* handshake was completed successfully by the driver. The BSSID is
- * specified with &NL80211_ATTR_MAC. Drivers that support 4 way handshake
+ * specified with %NL80211_ATTR_MAC. Drivers that support 4 way handshake
* offload should send this event after indicating 802.11 association with
- * &NL80211_CMD_CONNECT or &NL80211_CMD_ROAM. If the 4 way handshake failed
- * &NL80211_CMD_DISCONNECT should be indicated instead.
+ * %NL80211_CMD_CONNECT or %NL80211_CMD_ROAM. If the 4 way handshake failed
+ * %NL80211_CMD_DISCONNECT should be indicated instead.
*
* @NL80211_CMD_CONTROL_PORT_FRAME: Control Port (e.g. PAE) frame TX request
* and RX notification. This command is used both as a request to transmit
* initiated the connection through the connect request.
*
* @NL80211_CMD_STA_OPMODE_CHANGED: An event that notify station's
- * ht opmode or vht opmode changes using any of &NL80211_ATTR_SMPS_MODE,
- * &NL80211_ATTR_CHANNEL_WIDTH,&NL80211_ATTR_NSS attributes with its
- * address(specified in &NL80211_ATTR_MAC).
+ * ht opmode or vht opmode changes using any of %NL80211_ATTR_SMPS_MODE,
+ * %NL80211_ATTR_CHANNEL_WIDTH,%NL80211_ATTR_NSS attributes with its
+ * address(specified in %NL80211_ATTR_MAC).
*
* @NL80211_CMD_MAX: highest used command number
* @__NL80211_CMD_AFTER_LAST: internal use
* @NL80211_ATTR_EXTERNAL_AUTH_ACTION: Identify the requested external
* authentication operation (u32 attribute with an
* &enum nl80211_external_auth_action value). This is used with the
- * &NL80211_CMD_EXTERNAL_AUTH request event.
+ * %NL80211_CMD_EXTERNAL_AUTH request event.
* @NL80211_ATTR_EXTERNAL_AUTH_SUPPORT: Flag attribute indicating that the user
* space supports external authentication. This attribute shall be used
* only with %NL80211_CMD_CONNECT request. The driver may offload
* @NL80211_RRF_AUTO_BW: maximum available bandwidth should be calculated
* base on contiguous rules and wider channels will be allowed to cross
* multiple contiguous/overlapping frequency ranges.
- * @NL80211_RRF_IR_CONCURRENT: See &NL80211_FREQUENCY_ATTR_IR_CONCURRENT
+ * @NL80211_RRF_IR_CONCURRENT: See %NL80211_FREQUENCY_ATTR_IR_CONCURRENT
* @NL80211_RRF_NO_HT40MINUS: channels can't be used in HT40- operation
* @NL80211_RRF_NO_HT40PLUS: channels can't be used in HT40+ operation
* @NL80211_RRF_NO_80MHZ: 80MHz operation not allowed
* @NL80211_NAN_SRF_INCLUDE: present if the include bit of the SRF set.
* This is a flag.
* @NL80211_NAN_SRF_BF: Bloom Filter. Present if and only if
- * &NL80211_NAN_SRF_MAC_ADDRS isn't present. This attribute is binary.
+ * %NL80211_NAN_SRF_MAC_ADDRS isn't present. This attribute is binary.
* @NL80211_NAN_SRF_BF_IDX: index of the Bloom Filter. Mandatory if
- * &NL80211_NAN_SRF_BF is present. This is a u8.
+ * %NL80211_NAN_SRF_BF is present. This is a u8.
* @NL80211_NAN_SRF_MAC_ADDRS: list of MAC addresses for the SRF. Present if
- * and only if &NL80211_NAN_SRF_BF isn't present. This is a nested
+ * and only if %NL80211_NAN_SRF_BF isn't present. This is a nested
* attribute. Each nested attribute is a MAC address.
* @NUM_NL80211_NAN_SRF_ATTR: internal
* @NL80211_NAN_SRF_ATTR_MAX: highest NAN SRF attribute
* asking selinux for a specific new context (e.g. with runcon) will result
* in execve returning -EPERM.
*
- * See Documentation/prctl/no_new_privs.txt for more details.
+ * See Documentation/userspace-api/no_new_privs.rst for more details.
*/
#define PR_SET_NO_NEW_PRIVS 38
#define PR_GET_NO_NEW_PRIVS 39
#define UAC_OUTPUT_TERMINAL_COMMUNICATION_SPEAKER 0x306
#define UAC_OUTPUT_TERMINAL_LOW_FREQ_EFFECTS_SPEAKER 0x307
+/* Terminals - 2.4 Bi-directional Terminal Types */
+#define UAC_BIDIR_TERMINAL_UNDEFINED 0x400
+#define UAC_BIDIR_TERMINAL_HANDSET 0x401
+#define UAC_BIDIR_TERMINAL_HEADSET 0x402
+#define UAC_BIDIR_TERMINAL_SPEAKER_PHONE 0x403
+#define UAC_BIDIR_TERMINAL_ECHO_SUPPRESSING 0x404
+#define UAC_BIDIR_TERMINAL_ECHO_CANCELING 0x405
+
/* Set bControlSize = 2 as default setting */
#define UAC_DT_FEATURE_UNIT_SIZE(ch) (7 + ((ch) + 1) * 2)
/* We've given up on this device. */
#define VIRTIO_CONFIG_S_FAILED 0x80
-/* Some virtio feature bits (currently bits 28 through 32) are reserved for the
- * transport being used (eg. virtio_ring), the rest are per-device feature
- * bits. */
+/*
+ * Virtio feature bits VIRTIO_TRANSPORT_F_START through
+ * VIRTIO_TRANSPORT_F_END are reserved for the transport
+ * being used (e.g. virtio_ring, virtio_pci etc.), the
+ * rest are per-device feature bits.
+ */
#define VIRTIO_TRANSPORT_F_START 28
-#define VIRTIO_TRANSPORT_F_END 34
+#define VIRTIO_TRANSPORT_F_END 38
#ifndef VIRTIO_CONFIG_NO_LEGACY
/* Do we get callbacks when the ring is completely used, even if we've
* this is for compatibility with legacy systems.
*/
#define VIRTIO_F_IOMMU_PLATFORM 33
+
+/*
+ * Does the device support Single Root I/O Virtualization?
+ */
+#define VIRTIO_F_SR_IOV 37
#endif /* _UAPI_LINUX_VIRTIO_CONFIG_H */
+++ /dev/null
-/*
- * Definitions for AUO-K190X framebuffer drivers
- *
- * Copyright (C) 2012 Heiko Stuebner <heiko@sntech.de>
- *
- * 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_VIDEO_AUO_K190XFB_H_
-#define _LINUX_VIDEO_AUO_K190XFB_H_
-
-/* Controller standby command needs a param */
-#define AUOK190X_QUIRK_STANDBYPARAM (1 << 0)
-
-/* Controller standby is completely broken */
-#define AUOK190X_QUIRK_STANDBYBROKEN (1 << 1)
-
-/*
- * Resolutions for the displays
- */
-#define AUOK190X_RESOLUTION_800_600 0
-#define AUOK190X_RESOLUTION_1024_768 1
-#define AUOK190X_RESOLUTION_600_800 4
-#define AUOK190X_RESOLUTION_768_1024 5
-
-/*
- * struct used by auok190x. board specific stuff comes from *board
- */
-struct auok190xfb_par {
- struct fb_info *info;
- struct auok190x_board *board;
-
- struct regulator *regulator;
-
- struct mutex io_lock;
- struct delayed_work work;
- wait_queue_head_t waitq;
- int resolution;
- int rotation;
- int consecutive_threshold;
- int update_cnt;
-
- /* panel and controller informations */
- int epd_type;
- int panel_size_int;
- int panel_size_float;
- int panel_model;
- int tcon_version;
- int lut_version;
-
- /* individual controller callbacks */
- void (*update_partial)(struct auok190xfb_par *par, u16 y1, u16 y2);
- void (*update_all)(struct auok190xfb_par *par);
- bool (*need_refresh)(struct auok190xfb_par *par);
- void (*init)(struct auok190xfb_par *par);
- void (*recover)(struct auok190xfb_par *par);
-
- int update_mode; /* mode to use for updates */
- int last_mode; /* update mode last used */
- int flash;
-
- /* power management */
- int autosuspend_delay;
- bool standby;
- bool manual_standby;
-};
-
-/**
- * Board specific platform-data
- * @init: initialize the controller interface
- * @cleanup: cleanup the controller interface
- * @wait_for_rdy: wait until the controller is not busy anymore
- * @set_ctl: change an interface control
- * @set_hdb: write a value to the data register
- * @get_hdb: read a value from the data register
- * @setup_irq: method to setup the irq handling on the busy gpio
- * @gpio_nsleep: sleep gpio
- * @gpio_nrst: reset gpio
- * @gpio_nbusy: busy gpio
- * @resolution: one of the AUOK190X_RESOLUTION constants
- * @rotation: rotation of the framebuffer
- * @quirks: controller quirks to honor
- * @fps: frames per second for defio
- */
-struct auok190x_board {
- int (*init)(struct auok190xfb_par *);
- void (*cleanup)(struct auok190xfb_par *);
- int (*wait_for_rdy)(struct auok190xfb_par *);
-
- void (*set_ctl)(struct auok190xfb_par *, unsigned char, u8);
- void (*set_hdb)(struct auok190xfb_par *, u16);
- u16 (*get_hdb)(struct auok190xfb_par *);
-
- int (*setup_irq)(struct fb_info *);
-
- int gpio_nsleep;
- int gpio_nrst;
- int gpio_nbusy;
-
- int resolution;
- int quirks;
- int fps;
-};
-
-#endif
#define __ASM_SH_MOBILE_LCDC_H__
#include <linux/fb.h>
-#include <video/sh_mobile_meram.h>
/* Register definitions */
#define _LDDCKR 0x410
struct sh_mobile_lcdc_panel_cfg panel_cfg;
struct sh_mobile_lcdc_bl_info bl_info;
struct sh_mobile_lcdc_sys_bus_cfg sys_bus_cfg; /* only for SYSn I/F */
- const struct sh_mobile_meram_cfg *meram_cfg;
struct platform_device *tx_dev; /* HDMI/DSI transmitter device */
};
int clock_source;
struct sh_mobile_lcdc_chan_cfg ch[2];
struct sh_mobile_lcdc_overlay_cfg overlays[4];
- struct sh_mobile_meram_info *meram_dev;
};
#endif /* __ASM_SH_MOBILE_LCDC_H__ */
+++ /dev/null
-/* SPDX-License-Identifier: GPL-2.0 */
-#ifndef __VIDEO_SH_MOBILE_MERAM_H__
-#define __VIDEO_SH_MOBILE_MERAM_H__
-
-/* For sh_mobile_meram_info.addr_mode */
-enum {
- SH_MOBILE_MERAM_MODE0 = 0,
- SH_MOBILE_MERAM_MODE1
-};
-
-enum {
- SH_MOBILE_MERAM_PF_NV = 0,
- SH_MOBILE_MERAM_PF_RGB,
- SH_MOBILE_MERAM_PF_NV24
-};
-
-
-struct sh_mobile_meram_priv;
-
-/*
- * struct sh_mobile_meram_info - MERAM platform data
- * @reserved_icbs: Bitmask of reserved ICBs (for instance used through UIO)
- */
-struct sh_mobile_meram_info {
- int addr_mode;
- u32 reserved_icbs;
- struct sh_mobile_meram_priv *priv;
- struct platform_device *pdev;
-};
-
-/* icb config */
-struct sh_mobile_meram_icb_cfg {
- unsigned int meram_size; /* MERAM Buffer Size to use */
-};
-
-struct sh_mobile_meram_cfg {
- struct sh_mobile_meram_icb_cfg icb[2];
-};
-
-#if defined(CONFIG_FB_SH_MOBILE_MERAM) || \
- defined(CONFIG_FB_SH_MOBILE_MERAM_MODULE)
-unsigned long sh_mobile_meram_alloc(struct sh_mobile_meram_info *meram_dev,
- size_t size);
-void sh_mobile_meram_free(struct sh_mobile_meram_info *meram_dev,
- unsigned long mem, size_t size);
-void *sh_mobile_meram_cache_alloc(struct sh_mobile_meram_info *dev,
- const struct sh_mobile_meram_cfg *cfg,
- unsigned int xres, unsigned int yres,
- unsigned int pixelformat,
- unsigned int *pitch);
-void sh_mobile_meram_cache_free(struct sh_mobile_meram_info *dev, void *data);
-void sh_mobile_meram_cache_update(struct sh_mobile_meram_info *dev, void *data,
- unsigned long base_addr_y,
- unsigned long base_addr_c,
- unsigned long *icb_addr_y,
- unsigned long *icb_addr_c);
-#else
-static inline unsigned long
-sh_mobile_meram_alloc(struct sh_mobile_meram_info *meram_dev, size_t size)
-{
- return 0;
-}
-
-static inline void
-sh_mobile_meram_free(struct sh_mobile_meram_info *meram_dev,
- unsigned long mem, size_t size)
-{
-}
-
-static inline void *
-sh_mobile_meram_cache_alloc(struct sh_mobile_meram_info *dev,
- const struct sh_mobile_meram_cfg *cfg,
- unsigned int xres, unsigned int yres,
- unsigned int pixelformat,
- unsigned int *pitch)
-{
- return ERR_PTR(-ENODEV);
-}
-
-static inline void
-sh_mobile_meram_cache_free(struct sh_mobile_meram_info *dev, void *data)
-{
-}
-
-static inline void
-sh_mobile_meram_cache_update(struct sh_mobile_meram_info *dev, void *data,
- unsigned long base_addr_y,
- unsigned long base_addr_c,
- unsigned long *icb_addr_y,
- unsigned long *icb_addr_c)
-{
-}
-#endif
-
-#endif /* __VIDEO_SH_MOBILE_MERAM_H__ */
* Linux [2] and Windows [3] multi-touch support.
*
* [1] https://cgit.freedesktop.org/wayland/wayland/tree/protocol/wayland.xml
- * [2] https://www.kernel.org/doc/Documentation/input/multi-touch-protocol.txt
+ * [2] https://www.kernel.org/doc/Documentation/input/multi-touch-protocol.rst
* [3] https://msdn.microsoft.com/en-us/library/jj151564(v=vs.85).aspx
*
*
#include <linux/nsproxy.h>
#include <linux/ipc_namespace.h>
#include <linux/sched/wake_q.h>
+#include <linux/nospec.h>
#include <linux/uaccess.h>
#include "util.h"
int nsops)
{
struct sem *sem;
+ int idx;
if (nsops != 1) {
/* Complex operation - acquire a full lock */
*
* Both facts are tracked by use_global_mode.
*/
- sem = &sma->sems[sops->sem_num];
+ idx = array_index_nospec(sops->sem_num, sma->sem_nsems);
+ sem = &sma->sems[idx];
/*
* Initial check for use_global_lock. Just an optimization,
un = q->undo;
for (sop = sops; sop < sops + nsops; sop++) {
- curr = &sma->sems[sop->sem_num];
+ int idx = array_index_nospec(sop->sem_num, sma->sem_nsems);
+ curr = &sma->sems[idx];
sem_op = sop->sem_op;
result = curr->semval;
* until the operations can go through.
*/
for (sop = sops; sop < sops + nsops; sop++) {
- curr = &sma->sems[sop->sem_num];
+ int idx = array_index_nospec(sop->sem_num, sma->sem_nsems);
+
+ curr = &sma->sems[idx];
sem_op = sop->sem_op;
result = curr->semval;
return -EIDRM;
}
+ semnum = array_index_nospec(semnum, sma->sem_nsems);
curr = &sma->sems[semnum];
ipc_assert_locked_object(&sma->sem_perm);
err = -EIDRM;
goto out_unlock;
}
+
+ semnum = array_index_nospec(semnum, nsems);
curr = &sma->sems[semnum];
switch (cmd) {
*/
if (nsops == 1) {
struct sem *curr;
- curr = &sma->sems[sops->sem_num];
+ int idx = array_index_nospec(sops->sem_num, sma->sem_nsems);
+ curr = &sma->sems[idx];
if (alter) {
if (sma->complex_count) {
up_write(&shm_ids(ns).rwsem);
}
-static int shm_fault(struct vm_fault *vmf)
+static vm_fault_t shm_fault(struct vm_fault *vmf)
{
struct file *file = vmf->vma->vm_file;
struct shm_file_data *sfd = shm_file_data(file);
audit_update_mark(audit_mark, dentry->d_inode);
audit_mark->rule = krule;
- ret = fsnotify_add_mark(&audit_mark->mark, inode, NULL, true);
+ ret = fsnotify_add_inode_mark(&audit_mark->mark, inode, true);
if (ret < 0) {
fsnotify_put_mark(&audit_mark->mark);
audit_mark = ERR_PTR(ret);
/* Update mark data in audit rules based on fsnotify events. */
static int audit_mark_handle_event(struct fsnotify_group *group,
struct inode *to_tell,
- struct fsnotify_mark *inode_mark,
- struct fsnotify_mark *vfsmount_mark,
u32 mask, const void *data, int data_type,
const unsigned char *dname, u32 cookie,
struct fsnotify_iter_info *iter_info)
{
+ struct fsnotify_mark *inode_mark = fsnotify_iter_inode_mark(iter_info);
struct audit_fsnotify_mark *audit_mark;
const struct inode *inode = NULL;
if (!new)
goto Fallback;
- if (fsnotify_add_mark_locked(&new->mark, entry->connector->inode,
- NULL, 1)) {
+ if (fsnotify_add_inode_mark_locked(&new->mark, entry->connector->inode,
+ 1)) {
fsnotify_put_mark(&new->mark);
goto Fallback;
}
return -ENOMEM;
entry = &chunk->mark;
- if (fsnotify_add_mark(entry, inode, NULL, 0)) {
+ if (fsnotify_add_inode_mark(entry, inode, 0)) {
fsnotify_put_mark(entry);
return -ENOSPC;
}
return -ENOENT;
}
- if (fsnotify_add_mark_locked(chunk_entry,
- old_entry->connector->inode, NULL, 1)) {
+ if (fsnotify_add_inode_mark_locked(chunk_entry,
+ old_entry->connector->inode, 1)) {
spin_unlock(&old_entry->lock);
mutex_unlock(&old_entry->group->mark_mutex);
fsnotify_put_mark(chunk_entry);
static int audit_tree_handle_event(struct fsnotify_group *group,
struct inode *to_tell,
- struct fsnotify_mark *inode_mark,
- struct fsnotify_mark *vfsmount_mark,
u32 mask, const void *data, int data_type,
const unsigned char *file_name, u32 cookie,
struct fsnotify_iter_info *iter_info)
fsnotify_init_mark(&parent->mark, audit_watch_group);
parent->mark.mask = AUDIT_FS_WATCH;
- ret = fsnotify_add_mark(&parent->mark, inode, NULL, 0);
+ ret = fsnotify_add_inode_mark(&parent->mark, inode, 0);
if (ret < 0) {
audit_free_parent(parent);
return ERR_PTR(ret);
/* Update watch data in audit rules based on fsnotify events. */
static int audit_watch_handle_event(struct fsnotify_group *group,
struct inode *to_tell,
- struct fsnotify_mark *inode_mark,
- struct fsnotify_mark *vfsmount_mark,
u32 mask, const void *data, int data_type,
const unsigned char *dname, u32 cookie,
struct fsnotify_iter_info *iter_info)
{
+ struct fsnotify_mark *inode_mark = fsnotify_iter_inode_mark(iter_info);
const struct inode *inode;
struct audit_parent *parent;
.release = bpffs_map_release,
};
+static int bpffs_obj_open(struct inode *inode, struct file *file)
+{
+ return -EIO;
+}
+
+static const struct file_operations bpffs_obj_fops = {
+ .open = bpffs_obj_open,
+};
+
static int bpf_mkobj_ops(struct dentry *dentry, umode_t mode, void *raw,
const struct inode_operations *iops,
const struct file_operations *fops)
static int bpf_mkprog(struct dentry *dentry, umode_t mode, void *arg)
{
- return bpf_mkobj_ops(dentry, mode, arg, &bpf_prog_iops, NULL);
+ return bpf_mkobj_ops(dentry, mode, arg, &bpf_prog_iops,
+ &bpffs_obj_fops);
}
static int bpf_mkmap(struct dentry *dentry, umode_t mode, void *arg)
struct bpf_map *map = arg;
return bpf_mkobj_ops(dentry, mode, arg, &bpf_map_iops,
- map->btf ? &bpffs_map_fops : NULL);
+ map->btf ? &bpffs_map_fops : &bpffs_obj_fops);
}
static struct dentry *
* load balancing domains (sched domains) as specified by that partial
* partition.
*
- * See "What is sched_load_balance" in Documentation/cgroups/cpusets.txt
+ * See "What is sched_load_balance" in Documentation/cgroup-v1/cpusets.txt
* for a background explanation of this.
*
* Does not return errors, on the theory that the callers of this
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_RAM=y
CONFIG_BLK_DEV_RAM_SIZE=8192
-CONFIG_CC_STACKPROTECTOR_STRONG=y
+CONFIG_STACKPROTECTOR_STRONG=y
CONFIG_COMPACTION=y
CONFIG_CPU_SW_DOMAIN_PAN=y
CONFIG_DM_CRYPT=y
# CONFIG_SLAB is not set
# CONFIG_SLUB is not set
CONFIG_SLOB=y
-CONFIG_CC_STACKPROTECTOR_NONE=y
-# CONFIG_CC_STACKPROTECTOR_REGULAR is not set
-# CONFIG_CC_STACKPROTECTOR_STRONG is not set
-# CONFIG_CC_STACKPROTECTOR_AUTO is not set
continue;
}
charge = 0;
+ /*
+ * Don't duplicate many vmas if we've been oom-killed (for
+ * example)
+ */
+ if (fatal_signal_pending(current)) {
+ retval = -EINTR;
+ goto out;
+ }
if (mpnt->vm_flags & VM_ACCOUNT) {
unsigned long len = vma_pages(mpnt);
clear_tsk_need_resched(tsk);
set_task_stack_end_magic(tsk);
-#ifdef CONFIG_CC_STACKPROTECTOR
+#ifdef CONFIG_STACKPROTECTOR
tsk->stack_canary = get_random_canary();
#endif
static bool check_kcov_mode(enum kcov_mode needed_mode, struct task_struct *t)
{
- enum kcov_mode mode;
+ unsigned int mode;
/*
* We are interested in code coverage as a function of a syscall inputs,
void kcov_task_init(struct task_struct *t)
{
- t->kcov_mode = KCOV_MODE_DISABLED;
+ WRITE_ONCE(t->kcov_mode, KCOV_MODE_DISABLED);
+ barrier();
t->kcov_size = 0;
t->kcov_area = NULL;
t->kcov = NULL;
return 0;
}
+/*
+ * Fault in a lazily-faulted vmalloc area before it can be used by
+ * __santizer_cov_trace_pc(), to avoid recursion issues if any code on the
+ * vmalloc fault handling path is instrumented.
+ */
+static void kcov_fault_in_area(struct kcov *kcov)
+{
+ unsigned long stride = PAGE_SIZE / sizeof(unsigned long);
+ unsigned long *area = kcov->area;
+ unsigned long offset;
+
+ for (offset = 0; offset < kcov->size; offset += stride)
+ READ_ONCE(area[offset]);
+}
+
static int kcov_ioctl_locked(struct kcov *kcov, unsigned int cmd,
unsigned long arg)
{
#endif
else
return -EINVAL;
+ kcov_fault_in_area(kcov);
/* Cache in task struct for performance. */
t->kcov_size = kcov->size;
t->kcov_area = kcov->area;
else
buf += mchunk;
mbytes -= mchunk;
+
+ cond_resched();
}
out:
return result;
else
buf += mchunk;
mbytes -= mchunk;
+
+ cond_resched();
}
out:
return result;
}
static bool sig_enforce = IS_ENABLED(CONFIG_MODULE_SIG_FORCE);
-#ifndef CONFIG_MODULE_SIG_FORCE
module_param(sig_enforce, bool_enable_only, 0644);
-#endif /* !CONFIG_MODULE_SIG_FORCE */
/*
* Export sig_enforce kernel cmdline parameter to allow other subsystems rely
}
/* Not having a signature is only an error if we're strict. */
- if (err == -ENOKEY && !sig_enforce)
+ if (err == -ENOKEY && !is_module_sig_enforced())
err = 0;
return err;
device_initcall(register_warn_debugfs);
#endif
-#ifdef CONFIG_CC_STACKPROTECTOR
+#ifdef CONFIG_STACKPROTECTOR
/*
* Called when gcc's -fstack-protector feature is used, and
* state - control system sleep states.
*
* show() returns available sleep state labels, which may be "mem", "standby",
- * "freeze" and "disk" (hibernation). See Documentation/power/states.txt for a
- * description of what they mean.
+ * "freeze" and "disk" (hibernation).
+ * See Documentation/admin-guide/pm/sleep-states.rst for a description of
+ * what they mean.
*
* store() accepts one of those strings, translates it into the proper
* enumerated value, and initiates a suspend transition.
/*
* fault() vm_op implementation for relay file mapping.
*/
-static int relay_buf_fault(struct vm_fault *vmf)
+static vm_fault_t relay_buf_fault(struct vm_fault *vmf)
{
struct page *page;
struct rchan_buf *buf = vmf->vma->vm_private_data;
#include <linux/kthread.h>
#include <linux/nospec.h>
+#include <linux/kcov.h>
+
#include <asm/switch_to.h>
#include <asm/tlb.h>
prepare_task_switch(struct rq *rq, struct task_struct *prev,
struct task_struct *next)
{
+ kcov_prepare_switch(prev);
sched_info_switch(rq, prev, next);
perf_event_task_sched_out(prev, next);
rseq_preempt(prev);
finish_task(prev);
finish_lock_switch(rq);
finish_arch_post_lock_switch();
+ kcov_finish_switch(current);
fire_sched_in_preempt_notifiers(current);
/*
config HAVE_FTRACE_NMI_ENTER
bool
help
- See Documentation/trace/ftrace-design.txt
+ See Documentation/trace/ftrace-design.rst
config HAVE_FUNCTION_TRACER
bool
help
- See Documentation/trace/ftrace-design.txt
+ See Documentation/trace/ftrace-design.rst
config HAVE_FUNCTION_GRAPH_TRACER
bool
help
- See Documentation/trace/ftrace-design.txt
+ See Documentation/trace/ftrace-design.rst
config HAVE_DYNAMIC_FTRACE
bool
help
- See Documentation/trace/ftrace-design.txt
+ See Documentation/trace/ftrace-design.rst
config HAVE_DYNAMIC_FTRACE_WITH_REGS
bool
config HAVE_FTRACE_MCOUNT_RECORD
bool
help
- See Documentation/trace/ftrace-design.txt
+ See Documentation/trace/ftrace-design.rst
config HAVE_SYSCALL_TRACEPOINTS
bool
help
- See Documentation/trace/ftrace-design.txt
+ See Documentation/trace/ftrace-design.rst
config HAVE_FENTRY
bool
help
This allows the user to add tracing events (similar to tracepoints)
on the fly via the ftrace interface. See
- Documentation/trace/kprobetrace.txt for more details.
+ Documentation/trace/kprobetrace.rst for more details.
Those events can be inserted wherever kprobes can probe, and record
various register and memory values.
implementation and works via page faults. Tracing is disabled by
default and can be enabled at run-time.
- See Documentation/trace/mmiotrace.txt.
+ See Documentation/trace/mmiotrace.rst.
If you are not helping to develop drivers, say N.
config TRACING_MAP
See:
- Documentation/assoc_array.txt
+ Documentation/core-api/assoc_array.rst
for more information.
If unsure, say N.
+config FUNCTION_ERROR_INJECTION
+ def_bool y
+ depends on HAVE_FUNCTION_ERROR_INJECTION && KPROBES
+
config FAULT_INJECTION
bool "Fault-injection framework"
depends on DEBUG_KERNEL
Provide fault-injection framework.
For more details, see Documentation/fault-injection/.
-config FUNCTION_ERROR_INJECTION
- def_bool y
- depends on HAVE_FUNCTION_ERROR_INJECTION && KPROBES
-
config FAILSLAB
bool "Fault-injection capability for kmalloc"
depends on FAULT_INJECTION
Only works with drivers that use the generic timeout handling,
for others it wont do anything.
-config FAIL_MMC_REQUEST
- bool "Fault-injection capability for MMC IO"
- depends on FAULT_INJECTION_DEBUG_FS && MMC
- help
- Provide fault-injection capability for MMC IO.
- This will make the mmc core return data errors. This is
- useful to test the error handling in the mmc block device
- and to test how the mmc host driver handles retries from
- the block device.
-
config FAIL_FUTEX
bool "Fault-injection capability for futexes"
select DEBUG_FS
help
Provide fault-injection capability for futexes.
+config FAULT_INJECTION_DEBUG_FS
+ bool "Debugfs entries for fault-injection capabilities"
+ depends on FAULT_INJECTION && SYSFS && DEBUG_FS
+ help
+ Enable configuration of fault-injection capabilities via debugfs.
+
config FAIL_FUNCTION
bool "Fault-injection capability for functions"
depends on FAULT_INJECTION_DEBUG_FS && FUNCTION_ERROR_INJECTION
an error value and have to handle it. This is useful to test the
error handling in various subsystems.
-config FAULT_INJECTION_DEBUG_FS
- bool "Debugfs entries for fault-injection capabilities"
- depends on FAULT_INJECTION && SYSFS && DEBUG_FS
+config FAIL_MMC_REQUEST
+ bool "Fault-injection capability for MMC IO"
+ depends on FAULT_INJECTION_DEBUG_FS && MMC
help
- Enable configuration of fault-injection capabilities via debugfs.
+ Provide fault-injection capability for MMC IO.
+ This will make the mmc core return data errors. This is
+ useful to test the error handling in the mmc block device
+ and to test how the mmc host driver handles retries from
+ the block device.
config FAULT_INJECTION_STACKTRACE_FILTER
bool "stacktrace filter for fault-injection capabilities"
{
int err;
+ /*
+ * Make sure crng is ready. Otherwise we get "(ptrval)" instead
+ * of a hashed address when printing '%p' in plain_hash() and
+ * plain_format().
+ */
+ wait_for_random_bytes();
+
err = plain_hash();
if (err) {
pr_warn("plain 'p' does not appear to be hashed\n");
struct dentry *root = debugfs_create_dir("cleancache", NULL);
if (root == NULL)
return -ENXIO;
- debugfs_create_u64("succ_gets", S_IRUGO, root, &cleancache_succ_gets);
- debugfs_create_u64("failed_gets", S_IRUGO,
- root, &cleancache_failed_gets);
- debugfs_create_u64("puts", S_IRUGO, root, &cleancache_puts);
- debugfs_create_u64("invalidates", S_IRUGO,
- root, &cleancache_invalidates);
+ debugfs_create_u64("succ_gets", 0444, root, &cleancache_succ_gets);
+ debugfs_create_u64("failed_gets", 0444, root, &cleancache_failed_gets);
+ debugfs_create_u64("puts", 0444, root, &cleancache_puts);
+ debugfs_create_u64("invalidates", 0444, root, &cleancache_invalidates);
#endif
return 0;
}
tmp = debugfs_create_dir(name, cma_debugfs_root);
- debugfs_create_file("alloc", S_IWUSR, tmp, cma,
- &cma_alloc_fops);
-
- debugfs_create_file("free", S_IWUSR, tmp, cma,
- &cma_free_fops);
-
- debugfs_create_file("base_pfn", S_IRUGO, tmp,
- &cma->base_pfn, &cma_debugfs_fops);
- debugfs_create_file("count", S_IRUGO, tmp,
- &cma->count, &cma_debugfs_fops);
- debugfs_create_file("order_per_bit", S_IRUGO, tmp,
- &cma->order_per_bit, &cma_debugfs_fops);
- debugfs_create_file("used", S_IRUGO, tmp, cma, &cma_used_fops);
- debugfs_create_file("maxchunk", S_IRUGO, tmp, cma, &cma_maxchunk_fops);
+ debugfs_create_file("alloc", 0200, tmp, cma, &cma_alloc_fops);
+ debugfs_create_file("free", 0200, tmp, cma, &cma_free_fops);
+ debugfs_create_file("base_pfn", 0444, tmp,
+ &cma->base_pfn, &cma_debugfs_fops);
+ debugfs_create_file("count", 0444, tmp, &cma->count, &cma_debugfs_fops);
+ debugfs_create_file("order_per_bit", 0444, tmp,
+ &cma->order_per_bit, &cma_debugfs_fops);
+ debugfs_create_file("used", 0444, tmp, cma, &cma_used_fops);
+ debugfs_create_file("maxchunk", 0444, tmp, cma, &cma_maxchunk_fops);
u32s = DIV_ROUND_UP(cma_bitmap_maxno(cma), BITS_PER_BYTE * sizeof(u32));
- debugfs_create_u32_array("bitmap", S_IRUGO, tmp, (u32*)cma->bitmap, u32s);
+ debugfs_create_u32_array("bitmap", 0444, tmp, (u32 *)cma->bitmap, u32s);
}
static int __init cma_debugfs_init(void)
return count;
}
-static DEVICE_ATTR(compact, S_IWUSR, NULL, sysfs_compact_node);
+static DEVICE_ATTR(compact, 0200, NULL, sysfs_compact_node);
int compaction_register_node(struct node *node)
{
return PAGE_SIZE - size;
}
-static DEVICE_ATTR(pools, S_IRUGO, show_pools, NULL);
+static DEVICE_ATTR(pools, 0444, show_pools, NULL);
/**
* dma_pool_create - Creates a pool of consistent memory blocks, for dma.
static int __init failslab_debugfs_init(void)
{
struct dentry *dir;
- umode_t mode = S_IFREG | S_IRUSR | S_IWUSR;
+ umode_t mode = S_IFREG | 0600;
dir = fault_create_debugfs_attr("failslab", NULL, &failslab.attr);
if (IS_ERR(dir))
struct dentry *root = debugfs_create_dir("frontswap", NULL);
if (root == NULL)
return -ENXIO;
- debugfs_create_u64("loads", S_IRUGO, root, &frontswap_loads);
- debugfs_create_u64("succ_stores", S_IRUGO, root, &frontswap_succ_stores);
- debugfs_create_u64("failed_stores", S_IRUGO, root,
- &frontswap_failed_stores);
- debugfs_create_u64("invalidates", S_IRUGO,
- root, &frontswap_invalidates);
+ debugfs_create_u64("loads", 0444, root, &frontswap_loads);
+ debugfs_create_u64("succ_stores", 0444, root, &frontswap_succ_stores);
+ debugfs_create_u64("failed_stores", 0444, root,
+ &frontswap_failed_stores);
+ debugfs_create_u64("invalidates", 0444, root, &frontswap_invalidates);
#endif
return 0;
}
#define SEQNR_MASK 0x0ff /* low bits of unstable tree seqnr */
#define UNSTABLE_FLAG 0x100 /* is a node of the unstable tree */
#define STABLE_FLAG 0x200 /* is listed from the stable tree */
+#define KSM_FLAG_MASK (SEQNR_MASK|UNSTABLE_FLAG|STABLE_FLAG)
+ /* to mask all the flags */
/* The stable and unstable tree heads */
static struct rb_root one_stable_tree[1] = { RB_ROOT };
anon_vma_lock_read(anon_vma);
anon_vma_interval_tree_foreach(vmac, &anon_vma->rb_root,
0, ULONG_MAX) {
+ unsigned long addr;
+
cond_resched();
vma = vmac->vma;
- if (rmap_item->address < vma->vm_start ||
- rmap_item->address >= vma->vm_end)
+
+ /* Ignore the stable/unstable/sqnr flags */
+ addr = rmap_item->address & ~KSM_FLAG_MASK;
+
+ if (addr < vma->vm_start || addr >= vma->vm_end)
continue;
/*
* Initially we examine only the vma which covers this
if (rwc->invalid_vma && rwc->invalid_vma(vma, rwc->arg))
continue;
- if (!rwc->rmap_one(page, vma,
- rmap_item->address, rwc->arg)) {
+ if (!rwc->rmap_one(page, vma, addr, rwc->arg)) {
anon_vma_unlock_read(anon_vma);
return;
}
struct dentry *root = debugfs_create_dir("memblock", NULL);
if (!root)
return -ENXIO;
- debugfs_create_file("memory", S_IRUGO, root, &memblock.memory, &memblock_debug_fops);
- debugfs_create_file("reserved", S_IRUGO, root, &memblock.reserved, &memblock_debug_fops);
+ debugfs_create_file("memory", 0444, root,
+ &memblock.memory, &memblock_debug_fops);
+ debugfs_create_file("reserved", 0444, root,
+ &memblock.reserved, &memblock_debug_fops);
#ifdef CONFIG_HAVE_MEMBLOCK_PHYS_MAP
- debugfs_create_file("physmem", S_IRUGO, root, &memblock.physmem, &memblock_debug_fops);
+ debugfs_create_file("physmem", 0444, root,
+ &memblock.physmem, &memblock_debug_fops);
#endif
return 0;
seq_printf(sf, "oom_kill_disable %d\n", memcg->oom_kill_disable);
seq_printf(sf, "under_oom %d\n", (bool)memcg->under_oom);
- seq_printf(sf, "oom_kill %lu\n", memcg_sum_events(memcg, OOM_KILL));
+ seq_printf(sf, "oom_kill %lu\n",
+ atomic_long_read(&memcg->memory_events[MEMCG_OOM_KILL]));
return 0;
}
atomic_long_read(&memcg->memory_events[MEMCG_MAX]));
seq_printf(m, "oom %lu\n",
atomic_long_read(&memcg->memory_events[MEMCG_OOM]));
- seq_printf(m, "oom_kill %lu\n", memcg_sum_events(memcg, OOM_KILL));
+ seq_printf(m, "oom_kill %lu\n",
+ atomic_long_read(&memcg->memory_events[MEMCG_OOM_KILL]));
return 0;
}
elow = memcg->memory.low;
parent = parent_mem_cgroup(memcg);
+ /* No parent means a non-hierarchical mode on v1 memcg */
+ if (!parent)
+ return MEMCG_PROT_NONE;
+
if (parent == root)
goto exit;
drop_rmap_locks(vma);
}
-#define LATENCY_LIMIT (64 * PAGE_SIZE)
-
unsigned long move_page_tables(struct vm_area_struct *vma,
unsigned long old_addr, struct vm_area_struct *new_vma,
unsigned long new_addr, unsigned long len,
next = (new_addr + PMD_SIZE) & PMD_MASK;
if (extent > next - new_addr)
extent = next - new_addr;
- if (extent > LATENCY_LIMIT)
- extent = LATENCY_LIMIT;
move_ptes(vma, old_pmd, old_addr, old_addr + extent, new_vma,
new_pmd, new_addr, need_rmap_locks, &need_flush);
}
/* Raise event before sending signal: task reaper must see this */
count_vm_event(OOM_KILL);
- count_memcg_event_mm(mm, OOM_KILL);
+ memcg_memory_event_mm(mm, MEMCG_OOM_KILL);
/*
* We should send SIGKILL before granting access to memory reserves
static int __init fail_page_alloc_debugfs(void)
{
- umode_t mode = S_IFREG | S_IRUSR | S_IWUSR;
+ umode_t mode = S_IFREG | 0600;
struct dentry *dir;
dir = fault_create_debugfs_attr("fail_page_alloc", NULL,
}
static struct bin_attribute page_idle_bitmap_attr =
- __BIN_ATTR(bitmap, S_IRUSR | S_IWUSR,
+ __BIN_ATTR(bitmap, 0600,
page_idle_bitmap_read, page_idle_bitmap_write, 0);
static struct bin_attribute *page_idle_bin_attrs[] = {
return 0;
}
- dentry = debugfs_create_file("page_owner", S_IRUSR, NULL,
- NULL, &proc_page_owner_operations);
+ dentry = debugfs_create_file("page_owner", 0400, NULL,
+ NULL, &proc_page_owner_operations);
return PTR_ERR_OR_ZERO(dentry);
}
if (len > PAGE_SIZE)
return -ENAMETOOLONG;
- inode = shmem_get_inode(dir->i_sb, dir, S_IFLNK|S_IRWXUGO, 0, VM_NORESERVE);
+ inode = shmem_get_inode(dir->i_sb, dir, S_IFLNK | 0777, 0,
+ VM_NORESERVE);
if (!inode)
return -ENOSPC;
sbinfo->max_blocks << (PAGE_SHIFT - 10));
if (sbinfo->max_inodes != shmem_default_max_inodes())
seq_printf(seq, ",nr_inodes=%lu", sbinfo->max_inodes);
- if (sbinfo->mode != (S_IRWXUGO | S_ISVTX))
+ if (sbinfo->mode != (0777 | S_ISVTX))
seq_printf(seq, ",mode=%03ho", sbinfo->mode);
if (!uid_eq(sbinfo->uid, GLOBAL_ROOT_UID))
seq_printf(seq, ",uid=%u",
if (!sbinfo)
return -ENOMEM;
- sbinfo->mode = S_IRWXUGO | S_ISVTX;
+ sbinfo->mode = 0777 | S_ISVTX;
sbinfo->uid = current_fsuid();
sbinfo->gid = current_fsgid();
sb->s_fs_info = sbinfo;
d_set_d_op(path.dentry, &anon_ops);
res = ERR_PTR(-ENOSPC);
- inode = shmem_get_inode(sb, NULL, S_IFREG | S_IRWXUGO, 0, flags);
+ inode = shmem_get_inode(sb, NULL, S_IFREG | 0777, 0, flags);
if (!inode)
goto put_memory;
s->memcg_params.root_cache = NULL;
RCU_INIT_POINTER(s->memcg_params.memcg_caches, NULL);
INIT_LIST_HEAD(&s->memcg_params.children);
+ s->memcg_params.dying = false;
}
static int init_memcg_params(struct kmem_cache *s,
* The memory cgroup could have been offlined while the cache
* creation work was pending.
*/
- if (memcg->kmem_state != KMEM_ONLINE)
+ if (memcg->kmem_state != KMEM_ONLINE || root_cache->memcg_params.dying)
goto out_unlock;
idx = memcg_cache_id(memcg);
WARN_ON_ONCE(s->memcg_params.deact_fn))
return;
+ if (s->memcg_params.root_cache->memcg_params.dying)
+ return;
+
/* pin memcg so that @s doesn't get destroyed in the middle */
css_get(&s->memcg_params.memcg->css);
return -EBUSY;
return 0;
}
+
+static void flush_memcg_workqueue(struct kmem_cache *s)
+{
+ mutex_lock(&slab_mutex);
+ s->memcg_params.dying = true;
+ mutex_unlock(&slab_mutex);
+
+ /*
+ * SLUB deactivates the kmem_caches through call_rcu_sched. Make
+ * sure all registered rcu callbacks have been invoked.
+ */
+ if (IS_ENABLED(CONFIG_SLUB))
+ rcu_barrier_sched();
+
+ /*
+ * SLAB and SLUB create memcg kmem_caches through workqueue and SLUB
+ * deactivates the memcg kmem_caches through workqueue. Make sure all
+ * previous workitems on workqueue are processed.
+ */
+ flush_workqueue(memcg_kmem_cache_wq);
+}
#else
static inline int shutdown_memcg_caches(struct kmem_cache *s)
{
return 0;
}
+
+static inline void flush_memcg_workqueue(struct kmem_cache *s)
+{
+}
#endif /* CONFIG_MEMCG && !CONFIG_SLOB */
void slab_kmem_cache_release(struct kmem_cache *s)
if (unlikely(!s))
return;
+ flush_memcg_workqueue(s);
+
get_online_cpus();
get_online_mems();
#if defined(CONFIG_SLAB) || defined(CONFIG_SLUB_DEBUG)
#ifdef CONFIG_SLAB
-#define SLABINFO_RIGHTS (S_IWUSR | S_IRUSR)
+#define SLABINFO_RIGHTS (0600)
#else
-#define SLABINFO_RIGHTS S_IRUSR
+#define SLABINFO_RIGHTS (0400)
#endif
static void print_slabinfo_header(struct seq_file *m)
static inline unsigned char swap_count(unsigned char ent)
{
- return ent & ~SWAP_HAS_CACHE; /* may include SWAP_HAS_CONT flag */
+ return ent & ~SWAP_HAS_CACHE; /* may include COUNT_CONTINUED flag */
}
/* returns 1 if swap entry is freed */
static int __init proc_vmalloc_init(void)
{
if (IS_ENABLED(CONFIG_NUMA))
- proc_create_seq_private("vmallocinfo", S_IRUSR, NULL,
+ proc_create_seq_private("vmallocinfo", 0400, NULL,
&vmalloc_op,
nr_node_ids * sizeof(unsigned int), NULL);
else
- proc_create_seq("vmallocinfo", S_IRUSR, NULL, &vmalloc_op);
+ proc_create_seq("vmallocinfo", 0400, NULL, &vmalloc_op);
return 0;
}
module_init(proc_vmalloc_init);
}
pool->stat_dentry = entry;
- entry = debugfs_create_file("classes", S_IFREG | S_IRUGO,
- pool->stat_dentry, pool, &zs_stats_size_fops);
+ entry = debugfs_create_file("classes", S_IFREG | 0444,
+ pool->stat_dentry, pool,
+ &zs_stats_size_fops);
if (!entry) {
pr_warn("%s: debugfs file entry <%s> creation failed\n",
name, "classes");
if (!zswap_debugfs_root)
return -ENOMEM;
- debugfs_create_u64("pool_limit_hit", S_IRUGO,
- zswap_debugfs_root, &zswap_pool_limit_hit);
- debugfs_create_u64("reject_reclaim_fail", S_IRUGO,
- zswap_debugfs_root, &zswap_reject_reclaim_fail);
- debugfs_create_u64("reject_alloc_fail", S_IRUGO,
- zswap_debugfs_root, &zswap_reject_alloc_fail);
- debugfs_create_u64("reject_kmemcache_fail", S_IRUGO,
- zswap_debugfs_root, &zswap_reject_kmemcache_fail);
- debugfs_create_u64("reject_compress_poor", S_IRUGO,
- zswap_debugfs_root, &zswap_reject_compress_poor);
- debugfs_create_u64("written_back_pages", S_IRUGO,
- zswap_debugfs_root, &zswap_written_back_pages);
- debugfs_create_u64("duplicate_entry", S_IRUGO,
- zswap_debugfs_root, &zswap_duplicate_entry);
- debugfs_create_u64("pool_total_size", S_IRUGO,
- zswap_debugfs_root, &zswap_pool_total_size);
- debugfs_create_atomic_t("stored_pages", S_IRUGO,
- zswap_debugfs_root, &zswap_stored_pages);
+ debugfs_create_u64("pool_limit_hit", 0444,
+ zswap_debugfs_root, &zswap_pool_limit_hit);
+ debugfs_create_u64("reject_reclaim_fail", 0444,
+ zswap_debugfs_root, &zswap_reject_reclaim_fail);
+ debugfs_create_u64("reject_alloc_fail", 0444,
+ zswap_debugfs_root, &zswap_reject_alloc_fail);
+ debugfs_create_u64("reject_kmemcache_fail", 0444,
+ zswap_debugfs_root, &zswap_reject_kmemcache_fail);
+ debugfs_create_u64("reject_compress_poor", 0444,
+ zswap_debugfs_root, &zswap_reject_compress_poor);
+ debugfs_create_u64("written_back_pages", 0444,
+ zswap_debugfs_root, &zswap_written_back_pages);
+ debugfs_create_u64("duplicate_entry", 0444,
+ zswap_debugfs_root, &zswap_duplicate_entry);
+ debugfs_create_u64("pool_total_size", 0444,
+ zswap_debugfs_root, &zswap_pool_total_size);
+ debugfs_create_atomic_t("stored_pages", 0444,
+ zswap_debugfs_root, &zswap_stored_pages);
debugfs_create_atomic_t("same_filled_pages", 0444,
- zswap_debugfs_root, &zswap_same_filled_pages);
+ zswap_debugfs_root, &zswap_same_filled_pages);
return 0;
}
watcher = xt_request_find_target(NFPROTO_BRIDGE, w->u.name, 0);
if (IS_ERR(watcher))
return PTR_ERR(watcher);
+
+ if (watcher->family != NFPROTO_BRIDGE) {
+ module_put(watcher->me);
+ return -ENOENT;
+ }
+
w->u.watcher = watcher;
par->target = watcher;
}
i = 0;
+ memset(&mtpar, 0, sizeof(mtpar));
+ memset(&tgpar, 0, sizeof(tgpar));
mtpar.net = tgpar.net = net;
mtpar.table = tgpar.table = name;
mtpar.entryinfo = tgpar.entryinfo = e;
goto cleanup_watchers;
}
+ /* Reject UNSPEC, xtables verdicts/return values are incompatible */
+ if (target->family != NFPROTO_BRIDGE) {
+ module_put(target->me);
+ ret = -ENOENT;
+ goto cleanup_watchers;
+ }
+
t->u.target = target;
if (t->u.target == &ebt_standard_target) {
if (gap < sizeof(struct ebt_standard_target)) {
compat_uptr_t ptr;
} u;
compat_uint_t match_size;
- compat_uint_t data[0];
+ compat_uint_t data[0] __attribute__ ((aligned (__alignof__(struct compat_ebt_replace))));
};
/* account for possible padding between match_size and ->data */
static int ebt_compat_entry_padsize(void)
{
- BUILD_BUG_ON(XT_ALIGN(sizeof(struct ebt_entry_match)) <
- COMPAT_XT_ALIGN(sizeof(struct compat_ebt_entry_mwt)));
- return (int) XT_ALIGN(sizeof(struct ebt_entry_match)) -
- COMPAT_XT_ALIGN(sizeof(struct compat_ebt_entry_mwt));
+ BUILD_BUG_ON(sizeof(struct ebt_entry_match) <
+ sizeof(struct compat_ebt_entry_mwt));
+ return (int) sizeof(struct ebt_entry_match) -
+ sizeof(struct compat_ebt_entry_mwt);
}
static int ebt_compat_match_offset(const struct xt_match *match,
if (!reject6_br_csum_ok(oldskb, hook))
return;
- nskb = alloc_skb(sizeof(struct iphdr) + sizeof(struct icmp6hdr) +
+ nskb = alloc_skb(sizeof(struct ipv6hdr) + sizeof(struct icmp6hdr) +
LL_MAX_HEADER + len, GFP_ATOMIC);
if (!nskb)
return;
static struct lock_class_key socket_class;
#endif
-/*
- * When skipping (ignoring) a block of input we read it into a "skip
- * buffer," which is this many bytes in size.
- */
-#define SKIP_BUF_SIZE 1024
-
static void queue_con(struct ceph_connection *con);
static void cancel_con(struct ceph_connection *con);
static void ceph_con_workfn(struct work_struct *);
return 0;
}
+/*
+ * If @buf is NULL, discard up to @len bytes.
+ */
static int ceph_tcp_recvmsg(struct socket *sock, void *buf, size_t len)
{
struct kvec iov = {buf, len};
struct msghdr msg = { .msg_flags = MSG_DONTWAIT | MSG_NOSIGNAL };
int r;
+ if (!buf)
+ msg.msg_flags |= MSG_TRUNC;
+
iov_iter_kvec(&msg.msg_iter, READ | ITER_KVEC, &iov, 1, len);
r = sock_recvmsg(sock, &msg, msg.msg_flags);
if (r == -EAGAIN)
con->state != CON_STATE_OPEN)
return 0;
-more:
- dout("try_write out_kvec_bytes %d\n", con->out_kvec_bytes);
-
/* open the socket first? */
if (con->state == CON_STATE_PREOPEN) {
BUG_ON(con->sock);
}
}
-more_kvec:
+more:
+ dout("try_write out_kvec_bytes %d\n", con->out_kvec_bytes);
BUG_ON(!con->sock);
/* kvec data queued? */
ret = write_partial_message_data(con);
if (ret == 1)
- goto more_kvec; /* we need to send the footer, too! */
+ goto more; /* we need to send the footer, too! */
if (ret == 0)
goto out;
if (ret < 0) {
return ret;
}
-
-
/*
* Read what we can from the socket.
*/
if (con->in_base_pos < 0) {
/*
* skipping + discarding content.
- *
- * FIXME: there must be a better way to do this!
*/
- static char buf[SKIP_BUF_SIZE];
- int skip = min((int) sizeof (buf), -con->in_base_pos);
-
- dout("skipping %d / %d bytes\n", skip, -con->in_base_pos);
- ret = ceph_tcp_recvmsg(con->sock, buf, skip);
+ ret = ceph_tcp_recvmsg(con->sock, NULL, -con->in_base_pos);
if (ret <= 0)
goto out;
+ dout("skipped %d / %d bytes\n", ret, -con->in_base_pos);
con->in_base_pos += ret;
if (con->in_base_pos)
goto more;
}
EXPORT_SYMBOL(osd_req_op_extent_dup_last);
-void osd_req_op_cls_init(struct ceph_osd_request *osd_req, unsigned int which,
+int osd_req_op_cls_init(struct ceph_osd_request *osd_req, unsigned int which,
u16 opcode, const char *class, const char *method)
{
struct ceph_osd_req_op *op = _osd_req_op_init(osd_req, which,
BUG_ON(opcode != CEPH_OSD_OP_CALL);
pagelist = kmalloc(sizeof (*pagelist), GFP_NOFS);
- BUG_ON(!pagelist);
+ if (!pagelist)
+ return -ENOMEM;
+
ceph_pagelist_init(pagelist);
op->cls.class_name = class;
osd_req_op_cls_request_info_pagelist(osd_req, which, pagelist);
op->indata_len = payload_len;
+ return 0;
}
EXPORT_SYMBOL(osd_req_op_cls_init);
truncate_size, truncate_seq);
}
- req->r_abort_on_full = true;
req->r_flags = flags;
req->r_base_oloc.pool = layout->pool_id;
req->r_base_oloc.pool_ns = ceph_try_get_string(layout->pool_ns);
DEFINE_RB_FUNCS(request, struct ceph_osd_request, r_tid, r_node)
DEFINE_RB_FUNCS(request_mc, struct ceph_osd_request, r_tid, r_mc_node)
+/*
+ * Call @fn on each OSD request as long as @fn returns 0.
+ */
+static void for_each_request(struct ceph_osd_client *osdc,
+ int (*fn)(struct ceph_osd_request *req, void *arg),
+ void *arg)
+{
+ struct rb_node *n, *p;
+
+ for (n = rb_first(&osdc->osds); n; n = rb_next(n)) {
+ struct ceph_osd *osd = rb_entry(n, struct ceph_osd, o_node);
+
+ for (p = rb_first(&osd->o_requests); p; ) {
+ struct ceph_osd_request *req =
+ rb_entry(p, struct ceph_osd_request, r_node);
+
+ p = rb_next(p);
+ if (fn(req, arg))
+ return;
+ }
+ }
+
+ for (p = rb_first(&osdc->homeless_osd.o_requests); p; ) {
+ struct ceph_osd_request *req =
+ rb_entry(p, struct ceph_osd_request, r_node);
+
+ p = rb_next(p);
+ if (fn(req, arg))
+ return;
+ }
+}
+
static bool osd_homeless(struct ceph_osd *osd)
{
return osd->o_osd == CEPH_HOMELESS_OSD;
bool recovery_deletes = ceph_osdmap_flag(osdc,
CEPH_OSDMAP_RECOVERY_DELETES);
enum calc_target_result ct_res;
- int ret;
t->epoch = osdc->osdmap->epoch;
pi = ceph_pg_pool_by_id(osdc->osdmap, t->base_oloc.pool);
}
}
- ret = __ceph_object_locator_to_pg(pi, &t->target_oid, &t->target_oloc,
- &pgid);
- if (ret) {
- WARN_ON(ret != -ENOENT);
- t->osd = CEPH_HOMELESS_OSD;
- ct_res = CALC_TARGET_POOL_DNE;
- goto out;
- }
+ __ceph_object_locator_to_pg(pi, &t->target_oid, &t->target_oloc, &pgid);
last_pgid.pool = pgid.pool;
last_pgid.seed = ceph_stable_mod(pgid.seed, t->pg_num, t->pg_num_mask);
struct ceph_osd_client *osdc = req->r_osdc;
struct ceph_osd *osd;
enum calc_target_result ct_res;
+ int err = 0;
bool need_send = false;
bool promoted = false;
- bool need_abort = false;
WARN_ON(req->r_tid);
dout("%s req %p wrlocked %d\n", __func__, req, wrlocked);
goto promote;
}
- if (osdc->osdmap->epoch < osdc->epoch_barrier) {
+ if (osdc->abort_err) {
+ dout("req %p abort_err %d\n", req, osdc->abort_err);
+ err = osdc->abort_err;
+ } else if (osdc->osdmap->epoch < osdc->epoch_barrier) {
dout("req %p epoch %u barrier %u\n", req, osdc->osdmap->epoch,
osdc->epoch_barrier);
req->r_t.paused = true;
(ceph_osdmap_flag(osdc, CEPH_OSDMAP_FULL) ||
pool_full(osdc, req->r_t.base_oloc.pool))) {
dout("req %p full/pool_full\n", req);
- pr_warn_ratelimited("FULL or reached pool quota\n");
- req->r_t.paused = true;
- maybe_request_map(osdc);
- if (req->r_abort_on_full)
- need_abort = true;
+ if (osdc->abort_on_full) {
+ err = -ENOSPC;
+ } else {
+ pr_warn_ratelimited("FULL or reached pool quota\n");
+ req->r_t.paused = true;
+ maybe_request_map(osdc);
+ }
} else if (!osd_homeless(osd)) {
need_send = true;
} else {
link_request(osd, req);
if (need_send)
send_request(req);
- else if (need_abort)
- complete_request(req, -ENOSPC);
+ else if (err)
+ complete_request(req, err);
mutex_unlock(&osd->lock);
- if (ct_res == CALC_TARGET_POOL_DNE)
+ if (!err && ct_res == CALC_TARGET_POOL_DNE)
send_map_check(req);
if (promoted)
static void __complete_request(struct ceph_osd_request *req)
{
- if (req->r_callback) {
- dout("%s req %p tid %llu cb %pf result %d\n", __func__, req,
- req->r_tid, req->r_callback, req->r_result);
+ dout("%s req %p tid %llu cb %pf result %d\n", __func__, req,
+ req->r_tid, req->r_callback, req->r_result);
+
+ if (req->r_callback)
req->r_callback(req);
- }
+ complete_all(&req->r_completion);
+ ceph_osdc_put_request(req);
+}
+
+static void complete_request_workfn(struct work_struct *work)
+{
+ struct ceph_osd_request *req =
+ container_of(work, struct ceph_osd_request, r_complete_work);
+
+ __complete_request(req);
}
/*
req->r_result = err;
finish_request(req);
- __complete_request(req);
- complete_all(&req->r_completion);
- ceph_osdc_put_request(req);
+
+ INIT_WORK(&req->r_complete_work, complete_request_workfn);
+ queue_work(req->r_osdc->completion_wq, &req->r_complete_work);
}
static void cancel_map_check(struct ceph_osd_request *req)
complete_request(req, err);
}
+static int abort_fn(struct ceph_osd_request *req, void *arg)
+{
+ int err = *(int *)arg;
+
+ abort_request(req, err);
+ return 0; /* continue iteration */
+}
+
+/*
+ * Abort all in-flight requests with @err and arrange for all future
+ * requests to be failed immediately.
+ */
+void ceph_osdc_abort_requests(struct ceph_osd_client *osdc, int err)
+{
+ dout("%s osdc %p err %d\n", __func__, osdc, err);
+ down_write(&osdc->lock);
+ for_each_request(osdc, abort_fn, &err);
+ osdc->abort_err = err;
+ up_write(&osdc->lock);
+}
+EXPORT_SYMBOL(ceph_osdc_abort_requests);
+
static void update_epoch_barrier(struct ceph_osd_client *osdc, u32 eb)
{
if (likely(eb > osdc->epoch_barrier)) {
}
EXPORT_SYMBOL(ceph_osdc_update_epoch_barrier);
+/*
+ * We can end up releasing caps as a result of abort_request().
+ * In that case, we probably want to ensure that the cap release message
+ * has an updated epoch barrier in it, so set the epoch barrier prior to
+ * aborting the first request.
+ */
+static int abort_on_full_fn(struct ceph_osd_request *req, void *arg)
+{
+ struct ceph_osd_client *osdc = req->r_osdc;
+ bool *victims = arg;
+
+ if ((req->r_flags & CEPH_OSD_FLAG_WRITE) &&
+ (ceph_osdmap_flag(osdc, CEPH_OSDMAP_FULL) ||
+ pool_full(osdc, req->r_t.base_oloc.pool))) {
+ if (!*victims) {
+ update_epoch_barrier(osdc, osdc->osdmap->epoch);
+ *victims = true;
+ }
+ abort_request(req, -ENOSPC);
+ }
+
+ return 0; /* continue iteration */
+}
+
/*
* Drop all pending requests that are stalled waiting on a full condition to
* clear, and complete them with ENOSPC as the return code. Set the
*/
static void ceph_osdc_abort_on_full(struct ceph_osd_client *osdc)
{
- struct rb_node *n;
bool victims = false;
- dout("enter abort_on_full\n");
-
- if (!ceph_osdmap_flag(osdc, CEPH_OSDMAP_FULL) && !have_pool_full(osdc))
- goto out;
-
- /* Scan list and see if there is anything to abort */
- for (n = rb_first(&osdc->osds); n; n = rb_next(n)) {
- struct ceph_osd *osd = rb_entry(n, struct ceph_osd, o_node);
- struct rb_node *m;
-
- m = rb_first(&osd->o_requests);
- while (m) {
- struct ceph_osd_request *req = rb_entry(m,
- struct ceph_osd_request, r_node);
- m = rb_next(m);
-
- if (req->r_abort_on_full) {
- victims = true;
- break;
- }
- }
- if (victims)
- break;
- }
-
- if (!victims)
- goto out;
-
- /*
- * Update the barrier to current epoch if it's behind that point,
- * since we know we have some calls to be aborted in the tree.
- */
- update_epoch_barrier(osdc, osdc->osdmap->epoch);
-
- for (n = rb_first(&osdc->osds); n; n = rb_next(n)) {
- struct ceph_osd *osd = rb_entry(n, struct ceph_osd, o_node);
- struct rb_node *m;
-
- m = rb_first(&osd->o_requests);
- while (m) {
- struct ceph_osd_request *req = rb_entry(m,
- struct ceph_osd_request, r_node);
- m = rb_next(m);
-
- if (req->r_abort_on_full &&
- (ceph_osdmap_flag(osdc, CEPH_OSDMAP_FULL) ||
- pool_full(osdc, req->r_t.target_oloc.pool)))
- abort_request(req, -ENOSPC);
- }
- }
-out:
- dout("return abort_on_full barrier=%u\n", osdc->epoch_barrier);
+ if (osdc->abort_on_full &&
+ (ceph_osdmap_flag(osdc, CEPH_OSDMAP_FULL) || have_pool_full(osdc)))
+ for_each_request(osdc, abort_on_full_fn, &victims);
}
static void check_pool_dne(struct ceph_osd_request *req)
up_read(&osdc->lock);
__complete_request(req);
- complete_all(&req->r_completion);
- ceph_osdc_put_request(req);
return;
fail_request:
if (ret)
goto out_put_req;
- osd_req_op_cls_init(req, 0, CEPH_OSD_OP_CALL, class, method);
+ ret = osd_req_op_cls_init(req, 0, CEPH_OSD_OP_CALL, class, method);
+ if (ret)
+ goto out_put_req;
+
if (req_page)
osd_req_op_cls_request_data_pages(req, 0, &req_page, req_len,
0, false, false);
if (!osdc->notify_wq)
goto out_msgpool_reply;
+ osdc->completion_wq = create_singlethread_workqueue("ceph-completion");
+ if (!osdc->completion_wq)
+ goto out_notify_wq;
+
schedule_delayed_work(&osdc->timeout_work,
osdc->client->options->osd_keepalive_timeout);
schedule_delayed_work(&osdc->osds_timeout_work,
return 0;
+out_notify_wq:
+ destroy_workqueue(osdc->notify_wq);
out_msgpool_reply:
ceph_msgpool_destroy(&osdc->msgpool_op_reply);
out_msgpool:
void ceph_osdc_stop(struct ceph_osd_client *osdc)
{
- flush_workqueue(osdc->notify_wq);
+ destroy_workqueue(osdc->completion_wq);
destroy_workqueue(osdc->notify_wq);
cancel_delayed_work_sync(&osdc->timeout_work);
cancel_delayed_work_sync(&osdc->osds_timeout_work);
* Should only be called with target_oid and target_oloc (as opposed to
* base_oid and base_oloc), since tiering isn't taken into account.
*/
-int __ceph_object_locator_to_pg(struct ceph_pg_pool_info *pi,
- const struct ceph_object_id *oid,
- const struct ceph_object_locator *oloc,
- struct ceph_pg *raw_pgid)
+void __ceph_object_locator_to_pg(struct ceph_pg_pool_info *pi,
+ const struct ceph_object_id *oid,
+ const struct ceph_object_locator *oloc,
+ struct ceph_pg *raw_pgid)
{
WARN_ON(pi->id != oloc->pool);
int nsl = oloc->pool_ns->len;
size_t total = nsl + 1 + oid->name_len;
- if (total > sizeof(stack_buf)) {
- buf = kmalloc(total, GFP_NOIO);
- if (!buf)
- return -ENOMEM;
- }
+ if (total > sizeof(stack_buf))
+ buf = kmalloc(total, GFP_NOIO | __GFP_NOFAIL);
memcpy(buf, oloc->pool_ns->str, nsl);
buf[nsl] = '\037';
memcpy(buf + nsl + 1, oid->name, oid->name_len);
oid->name, nsl, oloc->pool_ns->str,
raw_pgid->pool, raw_pgid->seed);
}
- return 0;
}
int ceph_object_locator_to_pg(struct ceph_osdmap *osdmap,
if (!pi)
return -ENOENT;
- return __ceph_object_locator_to_pg(pi, oid, oloc, raw_pgid);
+ __ceph_object_locator_to_pg(pi, oid, oloc, raw_pgid);
+ return 0;
}
EXPORT_SYMBOL(ceph_object_locator_to_pg);
EXPORT_SYMBOL(neigh_rand_reach_time);
-static bool neigh_del(struct neighbour *n, __u8 state,
+static bool neigh_del(struct neighbour *n, __u8 state, __u8 flags,
struct neighbour __rcu **np, struct neigh_table *tbl)
{
bool retval = false;
write_lock(&n->lock);
- if (refcount_read(&n->refcnt) == 1 && !(n->nud_state & state)) {
+ if (refcount_read(&n->refcnt) == 1 && !(n->nud_state & state) &&
+ !(n->flags & flags)) {
struct neighbour *neigh;
neigh = rcu_dereference_protected(n->next,
while ((n = rcu_dereference_protected(*np,
lockdep_is_held(&tbl->lock)))) {
if (n == ndel)
- return neigh_del(n, 0, np, tbl);
+ return neigh_del(n, 0, 0, np, tbl);
np = &n->next;
}
return false;
* - nobody refers to it.
* - it is not permanent
*/
- if (neigh_del(n, NUD_PERMANENT, np, tbl)) {
+ if (neigh_del(n, NUD_PERMANENT, NTF_EXT_LEARNED, np,
+ tbl)) {
shrunk = 1;
continue;
}
sock_valbool_flag(sk, SOCK_DBG, valbool);
break;
case SO_REUSEADDR:
- val = (valbool ? SK_CAN_REUSE : SK_NO_REUSE);
- if ((sk->sk_family == PF_INET || sk->sk_family == PF_INET6) &&
- inet_sk(sk)->inet_num &&
- (sk->sk_reuse != val)) {
- ret = (sk->sk_state == TCP_ESTABLISHED) ? -EISCONN : -EUCLEAN;
- break;
- }
- sk->sk_reuse = val;
+ sk->sk_reuse = (valbool ? SK_CAN_REUSE : SK_NO_REUSE);
break;
case SO_REUSEPORT:
- if ((sk->sk_family == PF_INET || sk->sk_family == PF_INET6) &&
- inet_sk(sk)->inet_num &&
- (sk->sk_reuseport != valbool)) {
- ret = (sk->sk_state == TCP_ESTABLISHED) ? -EISCONN : -EUCLEAN;
- break;
- }
sk->sk_reuseport = valbool;
break;
case SO_TYPE:
if (!skb->dev)
return NULL;
- pskb_trim_rcsum(skb, skb->len - 4);
+ if (pskb_trim_rcsum(skb, skb->len - 4))
+ return NULL;
return skb;
}
return -ENOMEM;
j = 0;
+ memset(&mtpar, 0, sizeof(mtpar));
mtpar.net = net;
mtpar.table = name;
mtpar.entryinfo = &e->ip;
reqsk_put(req);
goto discard_it;
}
+ if (tcp_checksum_complete(skb)) {
+ reqsk_put(req);
+ goto csum_error;
+ }
if (unlikely(sk->sk_state != TCP_LISTEN)) {
inet_csk_reqsk_queue_drop_and_put(sk, req);
goto lookup;
goto out_check_final;
}
- p = *head;
- th2 = tcp_hdr(p);
tcp_flag_word(th2) |= flags & (TCP_FLAG_FIN | TCP_FLAG_PSH);
out_check_final:
}
}
+ memset(&cfg, 0, sizeof(cfg));
cfg.valid_lft = min_t(__u32, ifp->valid_lft,
idev->cnf.temp_valid_lft + age);
cfg.preferred_lft = cnf_temp_preferred_lft + age - idev->desync_factor;
cfg.pfx = &addr;
cfg.scope = ipv6_addr_scope(cfg.pfx);
- cfg.rt_priority = 0;
ift = ipv6_add_addr(idev, &cfg, block, NULL);
if (IS_ERR(ift)) {
{
struct fib6_info *leaf = rcu_dereference_protected(fn->leaf,
lockdep_is_held(&rt->fib6_table->tb6_lock));
+ enum fib_event_type event = FIB_EVENT_ENTRY_ADD;
struct fib6_info *iter = NULL, *match = NULL;
struct fib6_info __rcu **ins;
int replace = (info->nlh &&
"Can not append to a REJECT route");
return -EINVAL;
}
+ event = FIB_EVENT_ENTRY_APPEND;
rt->fib6_nsiblings = match->fib6_nsiblings;
list_add_tail(&rt->fib6_siblings, &match->fib6_siblings);
match->fib6_nsiblings++;
* insert node
*/
if (!replace) {
- enum fib_event_type event;
-
if (!add)
pr_warn("NLM_F_CREATE should be set when creating new route\n");
add:
nlflags |= NLM_F_CREATE;
- event = append ? FIB_EVENT_ENTRY_APPEND : FIB_EVENT_ENTRY_ADD;
err = call_fib6_entry_notifiers(info->nl_net, event, rt,
extack);
if (err)
return -ENOMEM;
j = 0;
+ memset(&mtpar, 0, sizeof(mtpar));
mtpar.net = net;
mtpar.table = name;
mtpar.entryinfo = &e->ipv6;
const struct in6_addr *daddr, *saddr;
struct rt6_info *rt6 = (struct rt6_info *)dst;
- if (rt6->rt6i_flags & RTF_LOCAL)
- return;
-
if (dst_metric_locked(dst, RTAX_MTU))
return;
reqsk_put(req);
goto discard_it;
}
+ if (tcp_checksum_complete(skb)) {
+ reqsk_put(req);
+ goto csum_error;
+ }
if (unlikely(sk->sk_state != TCP_LISTEN)) {
inet_csk_reqsk_queue_drop_and_put(sk, req);
goto lookup;
goto out_tunnel;
}
+ /* L2TPv2 only accepts PPP pseudo-wires */
+ if (tunnel->version == 2 && cfg.pw_type != L2TP_PWTYPE_PPP) {
+ ret = -EPROTONOSUPPORT;
+ goto out_tunnel;
+ }
+
if (tunnel->version > 2) {
if (info->attrs[L2TP_ATTR_DATA_SEQ])
cfg.data_seq = nla_get_u8(info->attrs[L2TP_ATTR_DATA_SEQ]);
u32 session_id, peer_session_id;
bool drop_refcnt = false;
bool drop_tunnel = false;
+ bool new_session = false;
+ bool new_tunnel = false;
int ver = 2;
int fd;
.encap = L2TP_ENCAPTYPE_UDP,
.debug = 0,
};
+
+ /* Prevent l2tp_tunnel_register() from trying to set up
+ * a kernel socket.
+ */
+ if (fd < 0) {
+ error = -EBADF;
+ goto end;
+ }
+
error = l2tp_tunnel_create(sock_net(sk), fd, ver, tunnel_id, peer_tunnel_id, &tcfg, &tunnel);
if (error < 0)
goto end;
goto end;
}
drop_tunnel = true;
+ new_tunnel = true;
}
} else {
/* Error if we can't find the tunnel */
session = l2tp_session_get(sock_net(sk), tunnel, session_id);
if (session) {
drop_refcnt = true;
+
+ if (session->pwtype != L2TP_PWTYPE_PPP) {
+ error = -EPROTOTYPE;
+ goto end;
+ }
+
ps = l2tp_session_priv(session);
/* Using a pre-existing session is fine as long as it hasn't
/* Default MTU must allow space for UDP/L2TP/PPP headers */
cfg.mtu = 1500 - PPPOL2TP_HEADER_OVERHEAD;
cfg.mru = cfg.mtu;
+ cfg.pw_type = L2TP_PWTYPE_PPP;
session = l2tp_session_create(sizeof(struct pppol2tp_session),
tunnel, session_id,
goto end;
}
drop_refcnt = true;
+ new_session = true;
}
/* Special case: if source & dest session_id == 0x0000, this
session->name);
end:
+ if (error) {
+ if (new_session)
+ l2tp_session_delete(session);
+ if (new_tunnel)
+ l2tp_tunnel_delete(tunnel);
+ }
if (drop_refcnt)
l2tp_session_dec_refcount(session);
if (drop_tunnel)
l2tp_session_get(sock_net(sk), tunnel,
stats.session_id);
- if (session) {
+ if (session && session->pwtype == L2TP_PWTYPE_PPP) {
err = pppol2tp_session_ioctl(session, cmd,
arg);
l2tp_session_dec_refcount(session);
ieee80211_led_init(local);
+ result = ieee80211_txq_setup_flows(local);
+ if (result)
+ goto fail_flows;
+
rtnl_lock();
result = ieee80211_init_rate_ctrl_alg(local,
rtnl_unlock();
- result = ieee80211_txq_setup_flows(local);
- if (result)
- goto fail_flows;
-
#ifdef CONFIG_INET
local->ifa_notifier.notifier_call = ieee80211_ifa_changed;
result = register_inetaddr_notifier(&local->ifa_notifier);
#if defined(CONFIG_INET) || defined(CONFIG_IPV6)
fail_ifa:
#endif
- ieee80211_txq_teardown_flows(local);
- fail_flows:
rtnl_lock();
rate_control_deinitialize(local);
ieee80211_remove_interfaces(local);
rtnl_unlock();
ieee80211_led_exit(local);
ieee80211_wep_free(local);
+ ieee80211_txq_teardown_flows(local);
+ fail_flows:
destroy_workqueue(local->workqueue);
fail_workqueue:
wiphy_unregister(local->hw.wiphy);
pr_debug("Create set %s with family %s\n",
set->name, set->family == NFPROTO_IPV4 ? "inet" : "inet6");
-#ifndef IP_SET_PROTO_UNDEF
+#ifdef IP_SET_PROTO_UNDEF
+ if (set->family != NFPROTO_UNSPEC)
+ return -IPSET_ERR_INVALID_FAMILY;
+#else
if (!(set->family == NFPROTO_IPV4 || set->family == NFPROTO_IPV6))
return -IPSET_ERR_INVALID_FAMILY;
#endif
* For now only for NAT!
*/
ip_vs_rs_hash(ipvs, dest);
+ /* FTP-NAT requires conntrack for mangling */
+ if (svc->port == FTPPORT)
+ ip_vs_register_conntrack(svc);
}
atomic_set(&dest->conn_flags, conn_flags);
*/
static void ip_vs_unlink_service(struct ip_vs_service *svc, bool cleanup)
{
+ ip_vs_unregister_conntrack(svc);
/* Hold svc to avoid double release from dest_trash */
atomic_inc(&svc->refcnt);
/*
bool new_rt_is_local)
{
bool rt_mode_allow_local = !!(rt_mode & IP_VS_RT_MODE_LOCAL);
- bool rt_mode_allow_non_local = !!(rt_mode & IP_VS_RT_MODE_LOCAL);
+ bool rt_mode_allow_non_local = !!(rt_mode & IP_VS_RT_MODE_NON_LOCAL);
bool rt_mode_allow_redirect = !!(rt_mode & IP_VS_RT_MODE_RDR);
bool source_is_loopback;
bool old_rt_is_local;
struct nf_conncount_tuple {
struct hlist_node node;
struct nf_conntrack_tuple tuple;
+ struct nf_conntrack_zone zone;
};
struct nf_conncount_rb {
}
bool nf_conncount_add(struct hlist_head *head,
- const struct nf_conntrack_tuple *tuple)
+ const struct nf_conntrack_tuple *tuple,
+ const struct nf_conntrack_zone *zone)
{
struct nf_conncount_tuple *conn;
if (conn == NULL)
return false;
conn->tuple = *tuple;
+ conn->zone = *zone;
hlist_add_head(&conn->node, head);
return true;
}
/* check the saved connections */
hlist_for_each_entry_safe(conn, n, head, node) {
- found = nf_conntrack_find_get(net, zone, &conn->tuple);
+ found = nf_conntrack_find_get(net, &conn->zone, &conn->tuple);
if (found == NULL) {
hlist_del(&conn->node);
kmem_cache_free(conncount_conn_cachep, conn);
found_ct = nf_ct_tuplehash_to_ctrack(found);
- if (tuple && nf_ct_tuple_equal(&conn->tuple, tuple)) {
+ if (tuple && nf_ct_tuple_equal(&conn->tuple, tuple) &&
+ nf_ct_zone_equal(found_ct, zone, zone->dir)) {
/*
* Just to be sure we have it only once in the list.
* We should not see tuples twice unless someone hooks
if (!addit)
return count;
- if (!nf_conncount_add(&rbconn->hhead, tuple))
+ if (!nf_conncount_add(&rbconn->hhead, tuple, zone))
return 0; /* hotdrop */
return count + 1;
}
conn->tuple = *tuple;
+ conn->zone = *zone;
memcpy(rbconn->key, key, sizeof(u32) * keylen);
INIT_HLIST_HEAD(&rbconn->hhead);
}
nfnl_lock(NFNL_SUBSYS_CTNETLINK);
rcu_read_lock();
- if (nat_hook->parse_nat_setup)
+ nat_hook = rcu_dereference(nf_nat_hook);
+ if (nat_hook)
return -EAGAIN;
#endif
return -EOPNOTSUPP;
u32 id = ntohl(nla_get_be32(nla));
list_for_each_entry(trans, &net->nft.commit_list, list) {
- struct nft_set *set = nft_trans_set(trans);
+ if (trans->msg_type == NFT_MSG_NEWSET) {
+ struct nft_set *set = nft_trans_set(trans);
- if (trans->msg_type == NFT_MSG_NEWSET &&
- id == nft_trans_set_id(trans) &&
- nft_active_genmask(set, genmask))
- return set;
+ if (id == nft_trans_set_id(trans) &&
+ nft_active_genmask(set, genmask))
+ return set;
+ }
}
return ERR_PTR(-ENOENT);
}
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
struct nft_flowtable *flowtable;
struct nft_table *table;
+ struct net *net;
if (event != NETDEV_UNREGISTER)
return 0;
+ net = maybe_get_net(dev_net(dev));
+ if (!net)
+ return 0;
+
nfnl_lock(NFNL_SUBSYS_NFTABLES);
- list_for_each_entry(table, &dev_net(dev)->nft.tables, list) {
+ list_for_each_entry(table, &net->nft.tables, list) {
list_for_each_entry(flowtable, &table->flowtables, list) {
nft_flowtable_event(event, dev, flowtable);
}
}
nfnl_unlock(NFNL_SUBSYS_NFTABLES);
-
+ put_net(net);
return NOTIFY_DONE;
}
kfree(trans);
}
-static int nf_tables_abort(struct net *net, struct sk_buff *skb)
+static int __nf_tables_abort(struct net *net)
{
struct nft_trans *trans, *next;
struct nft_trans_elem *te;
nft_validate_state_update(net, NFT_VALIDATE_SKIP);
}
+static int nf_tables_abort(struct net *net, struct sk_buff *skb)
+{
+ return __nf_tables_abort(net);
+}
+
static bool nf_tables_valid_genid(struct net *net, u32 genid)
{
return net->nft.base_seq == genid;
static void __net_exit nf_tables_exit_net(struct net *net)
{
+ nfnl_lock(NFNL_SUBSYS_NFTABLES);
+ if (!list_empty(&net->nft.commit_list))
+ __nf_tables_abort(net);
__nft_release_tables(net);
+ nfnl_unlock(NFNL_SUBSYS_NFTABLES);
WARN_ON_ONCE(!list_empty(&net->nft.tables));
- WARN_ON_ONCE(!list_empty(&net->nft.commit_list));
}
static struct pernet_operations nf_tables_net_ops = {
static void __exit nf_tables_module_exit(void)
{
- unregister_pernet_subsys(&nf_tables_net_ops);
nfnetlink_subsys_unregister(&nf_tables_subsys);
unregister_netdevice_notifier(&nf_tables_flowtable_notifier);
+ nft_chain_filter_fini();
+ unregister_pernet_subsys(&nf_tables_net_ops);
rcu_barrier();
nf_tables_core_module_exit();
kfree(info);
- nft_chain_filter_fini();
}
module_init(nf_tables_module_init);
switch (regs.verdict.code) {
case NFT_JUMP:
- BUG_ON(stackptr >= NFT_JUMP_STACK_SIZE);
+ if (WARN_ON_ONCE(stackptr >= NFT_JUMP_STACK_SIZE))
+ return NF_DROP;
jumpstack[stackptr].chain = chain;
jumpstack[stackptr].rules = rules + 1;
stackptr++;
*/
if (err == -EAGAIN) {
status |= NFNL_BATCH_REPLAY;
- goto next;
+ goto done;
}
}
ack:
if (err)
status |= NFNL_BATCH_FAILURE;
}
-next:
+
msglen = NLMSG_ALIGN(nlh->nlmsg_len);
if (msglen > skb->len)
msglen = skb->len;
}
done:
if (status & NFNL_BATCH_REPLAY) {
- ss->abort(net, oskb);
+ const struct nfnetlink_subsystem *ss2;
+
+ ss2 = nfnl_dereference_protected(subsys_id);
+ if (ss2 == ss)
+ ss->abort(net, oskb);
nfnl_err_reset(&err_list);
nfnl_unlock(subsys_id);
kfree_skb(skb);
event != NETDEV_CHANGENAME)
return NOTIFY_DONE;
+ ctx.net = maybe_get_net(ctx.net);
+ if (!ctx.net)
+ return NOTIFY_DONE;
+
nfnl_lock(NFNL_SUBSYS_NFTABLES);
list_for_each_entry(table, &ctx.net->nft.tables, list) {
if (table->family != NFPROTO_NETDEV)
}
}
nfnl_unlock(NFNL_SUBSYS_NFTABLES);
+ put_net(ctx.net);
return NOTIFY_DONE;
}
if (!addit)
goto out;
- if (!nf_conncount_add(&priv->hhead, tuple_ptr)) {
+ if (!nf_conncount_add(&priv->hhead, tuple_ptr, zone)) {
regs->verdict.code = NF_DROP;
spin_unlock_bh(&priv->lock);
return;
goto err1;
set->ops->gc_init(set);
}
-
- } else if (set->flags & NFT_SET_EVAL)
- return -EINVAL;
+ }
nft_set_ext_prepare(&priv->tmpl);
nft_set_ext_add_length(&priv->tmpl, NFT_SET_EXT_KEY, set->klen);
parent = rcu_dereference_raw(parent->rb_left);
if (interval &&
nft_rbtree_equal(set, this, interval) &&
- nft_rbtree_interval_end(this) &&
+ nft_rbtree_interval_end(rbe) &&
!nft_rbtree_interval_end(interval))
continue;
interval = rbe;
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Máté Eckl");
MODULE_DESCRIPTION("nf_tables socket match module");
+MODULE_ALIAS_NFT_EXPR("socket");
}
if (info->helper[0]) {
+ if (strnlen(info->helper, sizeof(info->helper)) == sizeof(info->helper)) {
+ ret = -ENAMETOOLONG;
+ goto err3;
+ }
+
ret = xt_ct_set_helper(ct, info->helper, par);
if (ret < 0)
goto err3;
}
if (info->timeout[0]) {
+ if (strnlen(info->timeout, sizeof(info->timeout)) == sizeof(info->timeout)) {
+ ret = -ENAMETOOLONG;
+ goto err4;
+ }
+
ret = xt_ct_set_timeout(ct, par, info->timeout);
if (ret < 0)
goto err4;
static void __exit connmark_mt_exit(void)
{
xt_unregister_match(&connmark_mt_reg);
- xt_unregister_target(connmark_tg_reg);
+ xt_unregister_targets(connmark_tg_reg, ARRAY_SIZE(connmark_tg_reg));
}
module_init(connmark_mt_init);
/* Normalize to fit into jiffies */
if (add_opt.ext.timeout != IPSET_NO_TIMEOUT &&
- add_opt.ext.timeout > UINT_MAX / MSEC_PER_SEC)
- add_opt.ext.timeout = UINT_MAX / MSEC_PER_SEC;
+ add_opt.ext.timeout > IPSET_MAX_TIMEOUT)
+ add_opt.ext.timeout = IPSET_MAX_TIMEOUT;
if (info->add_set.index != IPSET_INVALID_ID)
ip_set_add(info->add_set.index, skb, par, &add_opt);
if (info->del_set.index != IPSET_INVALID_ID)
/* Normalize to fit into jiffies */
if (add_opt.ext.timeout != IPSET_NO_TIMEOUT &&
- add_opt.ext.timeout > UINT_MAX / MSEC_PER_SEC)
- add_opt.ext.timeout = UINT_MAX / MSEC_PER_SEC;
+ add_opt.ext.timeout > IPSET_MAX_TIMEOUT)
+ add_opt.ext.timeout = IPSET_MAX_TIMEOUT;
if (info->add_set.index != IPSET_INVALID_ID)
ip_set_add(info->add_set.index, skb, par, &add_opt);
if (info->del_set.index != IPSET_INVALID_ID)
}
if (((info->flags & IPSET_FLAG_MAP_SKBPRIO) |
(info->flags & IPSET_FLAG_MAP_SKBQUEUE)) &&
- !(par->hook_mask & (1 << NF_INET_FORWARD |
+ (par->hook_mask & ~(1 << NF_INET_FORWARD |
1 << NF_INET_LOCAL_OUT |
1 << NF_INET_POST_ROUTING))) {
pr_info_ratelimited("mapping of prio or/and queue is allowed only from OUTPUT/FORWARD/POSTROUTING chains\n");
.inc_copy_to_user = rds_message_inc_copy_to_user,
.inc_free = rds_loop_inc_free,
.t_name = "loopback",
+ .t_type = RDS_TRANS_LOOP,
};
int n_status;
};
+/* Available as part of RDS core, so doesn't need to participate
+ * in get_preferred transport etc
+ */
+#define RDS_TRANS_LOOP 3
+
/**
* struct rds_transport - transport specific behavioural hooks
*
rds_stats_add(s_recv_bytes_added_to_socket, delta);
else
rds_stats_add(s_recv_bytes_removed_from_socket, -delta);
+
+ /* loop transport doesn't send/recv congestion updates */
+ if (rs->rs_transport->t_type == RDS_TRANS_LOOP)
+ return;
+
now_congested = rs->rs_rcv_bytes > rds_sk_rcvbuf(rs);
rdsdebug("rs %p (%pI4:%u) recv bytes %d buf %d "
refcount_inc(&sk->sk_wmem_alloc);
}
+static void sctp_packet_gso_append(struct sk_buff *head, struct sk_buff *skb)
+{
+ if (SCTP_OUTPUT_CB(head)->last == head)
+ skb_shinfo(head)->frag_list = skb;
+ else
+ SCTP_OUTPUT_CB(head)->last->next = skb;
+ SCTP_OUTPUT_CB(head)->last = skb;
+
+ head->truesize += skb->truesize;
+ head->data_len += skb->len;
+ head->len += skb->len;
+
+ __skb_header_release(skb);
+}
+
static int sctp_packet_pack(struct sctp_packet *packet,
struct sk_buff *head, int gso, gfp_t gfp)
{
if (gso) {
skb_shinfo(head)->gso_type = sk->sk_gso_type;
- NAPI_GRO_CB(head)->last = head;
+ SCTP_OUTPUT_CB(head)->last = head;
} else {
nskb = head;
pkt_size = packet->size;
&packet->chunk_list);
}
- if (gso) {
- if (skb_gro_receive(&head, nskb)) {
- kfree_skb(nskb);
- return 0;
- }
- if (WARN_ON_ONCE(skb_shinfo(head)->gso_segs >=
- sk->sk_gso_max_segs))
- return 0;
- }
+ if (gso)
+ sctp_packet_gso_append(head, nskb);
pkt_count++;
} while (!list_empty(&packet->chunk_list));
return mask;
}
-static __poll_t smc_poll(struct file *file, struct socket *sock,
- poll_table *wait)
+static __poll_t smc_poll_mask(struct socket *sock, __poll_t events)
{
struct sock *sk = sock->sk;
__poll_t mask = 0;
if ((sk->sk_state == SMC_INIT) || smc->use_fallback) {
/* delegate to CLC child sock */
release_sock(sk);
- mask = smc->clcsock->ops->poll(file, smc->clcsock, wait);
+ mask = smc->clcsock->ops->poll_mask(smc->clcsock, events);
lock_sock(sk);
sk->sk_err = smc->clcsock->sk->sk_err;
if (sk->sk_err) {
}
}
} else {
- if (sk->sk_state != SMC_CLOSED) {
- release_sock(sk);
- sock_poll_wait(file, sk_sleep(sk), wait);
- lock_sock(sk);
- }
if (sk->sk_err)
mask |= EPOLLERR;
if ((sk->sk_shutdown == SHUTDOWN_MASK) ||
.socketpair = sock_no_socketpair,
.accept = smc_accept,
.getname = smc_getname,
- .poll = smc_poll,
+ .poll_mask = smc_poll_mask,
.ioctl = smc_ioctl,
.listen = smc_listen,
.shutdown = smc_shutdown,
build_protos(tls_prots[TLSV4], &tcp_prot);
tls_sw_proto_ops = inet_stream_ops;
- tls_sw_proto_ops.poll = tls_sw_poll;
+ tls_sw_proto_ops.poll_mask = tls_sw_poll_mask;
tls_sw_proto_ops.splice_read = tls_sw_splice_read;
#ifdef CONFIG_TLS_DEVICE
}
static int tls_do_encryption(struct tls_context *tls_ctx,
- struct tls_sw_context_tx *ctx, size_t data_len,
- gfp_t flags)
+ struct tls_sw_context_tx *ctx,
+ struct aead_request *aead_req,
+ size_t data_len)
{
- unsigned int req_size = sizeof(struct aead_request) +
- crypto_aead_reqsize(ctx->aead_send);
- struct aead_request *aead_req;
int rc;
- aead_req = kzalloc(req_size, flags);
- if (!aead_req)
- return -ENOMEM;
-
ctx->sg_encrypted_data[0].offset += tls_ctx->tx.prepend_size;
ctx->sg_encrypted_data[0].length -= tls_ctx->tx.prepend_size;
ctx->sg_encrypted_data[0].offset -= tls_ctx->tx.prepend_size;
ctx->sg_encrypted_data[0].length += tls_ctx->tx.prepend_size;
- kfree(aead_req);
return rc;
}
{
struct tls_context *tls_ctx = tls_get_ctx(sk);
struct tls_sw_context_tx *ctx = tls_sw_ctx_tx(tls_ctx);
+ struct aead_request *req;
int rc;
+ req = kzalloc(sizeof(struct aead_request) +
+ crypto_aead_reqsize(ctx->aead_send), sk->sk_allocation);
+ if (!req)
+ return -ENOMEM;
+
sg_mark_end(ctx->sg_plaintext_data + ctx->sg_plaintext_num_elem - 1);
sg_mark_end(ctx->sg_encrypted_data + ctx->sg_encrypted_num_elem - 1);
tls_ctx->pending_open_record_frags = 0;
set_bit(TLS_PENDING_CLOSED_RECORD, &tls_ctx->flags);
- rc = tls_do_encryption(tls_ctx, ctx, ctx->sg_plaintext_size,
- sk->sk_allocation);
+ rc = tls_do_encryption(tls_ctx, ctx, req, ctx->sg_plaintext_size);
if (rc < 0) {
/* If we are called from write_space and
* we fail, we need to set this SOCK_NOSPACE
* to trigger another write_space in the future.
*/
set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
- return rc;
+ goto out_req;
}
free_sg(sk, ctx->sg_plaintext_data, &ctx->sg_plaintext_num_elem,
tls_err_abort(sk, EBADMSG);
tls_advance_record_sn(sk, &tls_ctx->tx);
+out_req:
+ kfree(req);
return rc;
}
struct sk_buff *skb;
ssize_t copied = 0;
bool cmsg = false;
- int err = 0;
+ int target, err = 0;
long timeo;
flags |= nonblock;
lock_sock(sk);
+ target = sock_rcvlowat(sk, flags & MSG_WAITALL, len);
timeo = sock_rcvtimeo(sk, flags & MSG_DONTWAIT);
do {
bool zc = false;
goto recv_end;
}
}
+ /* If we have a new message from strparser, continue now. */
+ if (copied >= target && !ctx->recv_pkt)
+ break;
} while (len);
recv_end:
return copied ? : err;
}
-unsigned int tls_sw_poll(struct file *file, struct socket *sock,
- struct poll_table_struct *wait)
+__poll_t tls_sw_poll_mask(struct socket *sock, __poll_t events)
{
- unsigned int ret;
struct sock *sk = sock->sk;
struct tls_context *tls_ctx = tls_get_ctx(sk);
struct tls_sw_context_rx *ctx = tls_sw_ctx_rx(tls_ctx);
+ __poll_t mask;
- /* Grab POLLOUT and POLLHUP from the underlying socket */
- ret = ctx->sk_poll(file, sock, wait);
+ /* Grab EPOLLOUT and EPOLLHUP from the underlying socket */
+ mask = ctx->sk_poll_mask(sock, events);
- /* Clear POLLIN bits, and set based on recv_pkt */
- ret &= ~(POLLIN | POLLRDNORM);
+ /* Clear EPOLLIN bits, and set based on recv_pkt */
+ mask &= ~(EPOLLIN | EPOLLRDNORM);
if (ctx->recv_pkt)
- ret |= POLLIN | POLLRDNORM;
+ mask |= EPOLLIN | EPOLLRDNORM;
- return ret;
+ return mask;
}
static int tls_read_size(struct strparser *strp, struct sk_buff *skb)
sk->sk_data_ready = tls_data_ready;
write_unlock_bh(&sk->sk_callback_lock);
- sw_ctx_rx->sk_poll = sk->sk_socket->ops->poll;
+ sw_ctx_rx->sk_poll_mask = sk->sk_socket->ops->poll_mask;
strp_check_rcv(&sw_ctx_rx->strp);
}
nl80211_notify_iface(rdev, wdev, NL80211_CMD_DEL_INTERFACE);
list_del_rcu(&wdev->list);
+ synchronize_rcu();
rdev->devlist_generation++;
switch (wdev->iftype) {
if (!rdev->ops->get_station)
return -EOPNOTSUPP;
+ memset(sinfo, 0, sizeof(*sinfo));
+
return rdev_get_station(rdev, dev, mac_addr, sinfo);
}
EXPORT_SYMBOL(cfg80211_get_station);
long npgs;
int err;
- umem->pgs = kcalloc(umem->npgs, sizeof(*umem->pgs), GFP_KERNEL);
+ umem->pgs = kcalloc(umem->npgs, sizeof(*umem->pgs),
+ GFP_KERNEL | __GFP_NOWARN);
if (!umem->pgs)
return -ENOMEM;
);
if ($help != 0) {
- print "$scriptname [--help] [--fix-rst]\n";
+ print "$scriptname [--help] [--fix]\n";
exit -1;
}
my $f = $1;
my $ln = $2;
- # Makefiles contain nasty expressions to parse docs
- next if ($f =~ m/Makefile/);
+ # Makefiles and scripts contain nasty expressions to parse docs
+ next if ($f =~ m/Makefile/ || $f =~ m/\.sh$/);
+
# Skip this script
next if ($f eq $scriptname);
- if ($ln =~ m,\b(\S*)(Documentation/[A-Za-z0-9\_\.\,\~/\*+-]*),) {
+ if ($ln =~ m,\b(\S*)(Documentation/[A-Za-z0-9\_\.\,\~/\*\[\]\?+-]*)(.*),) {
my $prefix = $1;
my $ref = $2;
my $base = $2;
+ my $extra = $3;
+
+ # some file references are like:
+ # /usr/src/linux/Documentation/DMA-{API,mapping}.txt
+ # For now, ignore them
+ next if ($extra =~ m/^{/);
+
+ # Remove footnotes at the end like:
+ # Documentation/devicetree/dt-object-internal.txt[1]
+ $ref =~ s/(txt|rst)\[\d+]$/$1/;
+
+ # Remove ending ']' without any '['
+ $ref =~ s/\].*// if (!($ref =~ m/\[/));
+ # Remove puntuation marks at the end
$ref =~ s/[\,\.]+$//;
my $fulref = "$prefix$ref";
# Check if exists, evaluating wildcards
next if (grep -e, glob("$ref $fulref"));
+ # Accept relative Documentation patches for tools/
+ if ($f =~ m/tools/) {
+ my $path = $f;
+ $path =~ s,(.*)/.*,$1,;
+ next if (grep -e, glob("$path/$ref $path/$fulref"));
+ }
+
if ($fix) {
- if (!($ref =~ m/(devicetree|scripts|Kconfig|Kbuild)/)) {
+ if (!($ref =~ m/(scripts|Kconfig|Kbuild)/)) {
$broken_ref{$ref}++;
}
} else {
# get just the basename
$new =~ s,.*/,,;
- # Seek for the same name on another place, as it may have been moved
my $f="";
- $f = qx(find . -iname $new) if ($new);
+ # usual reason for breakage: DT file moved around
+ if ($ref =~ /devicetree/) {
+ my $search = $new;
+ $search =~ s,^.*/,,;
+ $f = qx(find Documentation/devicetree/ -iname "*$search*") if ($search);
+ if (!$f) {
+ # Manufacturer name may have changed
+ $search =~ s/^.*,//;
+ $f = qx(find Documentation/devicetree/ -iname "*$search*") if ($search);
+ }
+ }
# usual reason for breakage: file renamed to .rst
if (!$f) {
$f=qx(find . -iname $new) if ($new);
}
+ # usual reason for breakage: use dash or underline
+ if (!$f) {
+ $new =~ s/[-_]/[-_]/g;
+ $f=qx(find . -iname $new) if ($new);
+ }
+
+ # Wild guess: seek for the same name on another place
+ if (!$f) {
+ $f = qx(find . -iname $new) if ($new);
+ }
+
my @find = split /\s+/, $f;
if (!$f) {
#include "include/audit.h"
#include "include/policy.h"
#include "include/policy_ns.h"
-
+#include "include/secid.h"
const char *const audit_mode_names[] = {
"normal",
return aad(sa)->error;
}
+
+struct aa_audit_rule {
+ struct aa_label *label;
+};
+
+void aa_audit_rule_free(void *vrule)
+{
+ struct aa_audit_rule *rule = vrule;
+
+ if (rule) {
+ if (!IS_ERR(rule->label))
+ aa_put_label(rule->label);
+ kfree(rule);
+ }
+}
+
+int aa_audit_rule_init(u32 field, u32 op, char *rulestr, void **vrule)
+{
+ struct aa_audit_rule *rule;
+
+ switch (field) {
+ case AUDIT_SUBJ_ROLE:
+ if (op != Audit_equal && op != Audit_not_equal)
+ return -EINVAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ rule = kzalloc(sizeof(struct aa_audit_rule), GFP_KERNEL);
+
+ if (!rule)
+ return -ENOMEM;
+
+ /* Currently rules are treated as coming from the root ns */
+ rule->label = aa_label_parse(&root_ns->unconfined->label, rulestr,
+ GFP_KERNEL, true, false);
+ if (IS_ERR(rule->label)) {
+ aa_audit_rule_free(rule);
+ return PTR_ERR(rule->label);
+ }
+
+ *vrule = rule;
+ return 0;
+}
+
+int aa_audit_rule_known(struct audit_krule *rule)
+{
+ int i;
+
+ for (i = 0; i < rule->field_count; i++) {
+ struct audit_field *f = &rule->fields[i];
+
+ switch (f->type) {
+ case AUDIT_SUBJ_ROLE:
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+int aa_audit_rule_match(u32 sid, u32 field, u32 op, void *vrule,
+ struct audit_context *actx)
+{
+ struct aa_audit_rule *rule = vrule;
+ struct aa_label *label;
+ int found = 0;
+
+ label = aa_secid_to_label(sid);
+
+ if (!label)
+ return -ENOENT;
+
+ if (aa_label_is_subset(label, rule->label))
+ found = 1;
+
+ switch (field) {
+ case AUDIT_SUBJ_ROLE:
+ switch (op) {
+ case Audit_equal:
+ return found;
+ case Audit_not_equal:
+ return !found;
+ }
+ }
+ return 0;
+}
cond, unsafe));
} else {
- /* TODO: determine how much we want to losen this */
+ /* TODO: determine how much we want to loosen this */
error = fn_for_each_in_ns(label, profile,
profile_onexec(profile, onexec, stack, bprm,
buffer, cond, unsafe));
return error;
}
+void aa_audit_rule_free(void *vrule);
+int aa_audit_rule_init(u32 field, u32 op, char *rulestr, void **vrule);
+int aa_audit_rule_known(struct audit_krule *rule);
+int aa_audit_rule_match(u32 sid, u32 field, u32 op, void *vrule,
+ struct audit_context *actx);
+
#endif /* __AA_AUDIT_H */
void aa_label_free(struct aa_label *label);
void aa_label_kref(struct kref *kref);
-bool aa_label_init(struct aa_label *label, int size);
+bool aa_label_init(struct aa_label *label, int size, gfp_t gfp);
struct aa_label *aa_label_alloc(int size, struct aa_proxy *proxy, gfp_t gfp);
bool aa_label_is_subset(struct aa_label *set, struct aa_label *sub);
DECLARE_PER_CPU(struct aa_buffers, aa_buffers);
-#define ASSIGN(FN, X, N) ((X) = FN(N))
-#define EVAL1(FN, X) ASSIGN(FN, X, 0) /*X = FN(0)*/
-#define EVAL2(FN, X, Y...) do { ASSIGN(FN, X, 1); EVAL1(FN, Y); } while (0)
-#define EVAL(FN, X...) CONCATENATE(EVAL, COUNT_ARGS(X))(FN, X)
+#define ASSIGN(FN, A, X, N) ((X) = FN(A, N))
+#define EVAL1(FN, A, X) ASSIGN(FN, A, X, 0) /*X = FN(0)*/
+#define EVAL2(FN, A, X, Y...) \
+ do { ASSIGN(FN, A, X, 1); EVAL1(FN, A, Y); } while (0)
+#define EVAL(FN, A, X...) CONCATENATE(EVAL, COUNT_ARGS(X))(FN, A, X)
#define for_each_cpu_buffer(I) for ((I) = 0; (I) < MAX_PATH_BUFFERS; (I)++)
#define AA_BUG_PREEMPT_ENABLED(X) /* nop */
#endif
-#define __get_buffer(N) ({ \
- struct aa_buffers *__cpu_var; \
+#define __get_buffer(C, N) ({ \
AA_BUG_PREEMPT_ENABLED("__get_buffer without preempt disabled"); \
- __cpu_var = this_cpu_ptr(&aa_buffers); \
- __cpu_var->buf[(N)]; })
+ (C)->buf[(N)]; })
-#define __get_buffers(X...) EVAL(__get_buffer, X)
+#define __get_buffers(C, X...) EVAL(__get_buffer, C, X)
#define __put_buffers(X, Y...) ((void)&(X))
-#define get_buffers(X...) \
-do { \
- preempt_disable(); \
- __get_buffers(X); \
+#define get_buffers(X...) \
+do { \
+ struct aa_buffers *__cpu_var = get_cpu_ptr(&aa_buffers); \
+ __get_buffers(__cpu_var, X); \
} while (0)
-#define put_buffers(X, Y...) \
-do { \
- __put_buffers(X, Y); \
- preempt_enable(); \
+#define put_buffers(X, Y...) \
+do { \
+ __put_buffers(X, Y); \
+ put_cpu_ptr(&aa_buffers); \
} while (0)
#endif /* __AA_PATH_H */
*
* This file contains AppArmor security identifier (secid) definitions
*
- * Copyright 2009-2010 Canonical Ltd.
+ * Copyright 2009-2018 Canonical Ltd.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
#ifndef __AA_SECID_H
#define __AA_SECID_H
+#include <linux/slab.h>
#include <linux/types.h>
+struct aa_label;
+
/* secid value that will not be allocated */
#define AA_SECID_INVALID 0
-#define AA_SECID_ALLOC AA_SECID_INVALID
-u32 aa_alloc_secid(void);
+struct aa_label *aa_secid_to_label(u32 secid);
+int apparmor_secid_to_secctx(u32 secid, char **secdata, u32 *seclen);
+int apparmor_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid);
+void apparmor_release_secctx(char *secdata, u32 seclen);
+
+
+int aa_alloc_secid(struct aa_label *label, gfp_t gfp);
void aa_free_secid(u32 secid);
+void aa_secid_update(u32 secid, struct aa_label *label);
+
+void aa_secids_init(void);
#endif /* __AA_SECID_H */
}
/**
- * profile_cmp - profile comparision for set ordering
+ * profile_cmp - profile comparison for set ordering
* @a: profile to compare (NOT NULL)
* @b: profile to compare (NOT NULL)
*
}
/**
- * vec_cmp - label comparision for set ordering
+ * vec_cmp - label comparison for set ordering
* @a: label to compare (NOT NULL)
* @vec: vector of profiles to compare (NOT NULL)
* @n: length of @vec
aa_put_label(new);
}
-bool aa_label_init(struct aa_label *label, int size)
+bool aa_label_init(struct aa_label *label, int size, gfp_t gfp)
{
AA_BUG(!label);
AA_BUG(size < 1);
- label->secid = aa_alloc_secid();
- if (label->secid == AA_SECID_INVALID)
+ if (aa_alloc_secid(label, gfp) < 0)
return false;
label->size = size; /* doesn't include null */
if (!new)
goto fail;
- if (!aa_label_init(new, size))
+ if (!aa_label_init(new, size, gfp))
goto fail;
if (!proxy) {
/**
- * label_cmp - label comparision for set ordering
+ * label_cmp - label comparison for set ordering
* @a: label to compare (NOT NULL)
* @b: label to compare (NOT NULL)
*
/**
* __label_update - insert updated version of @label into labelset
- * @label - the label to update/repace
+ * @label - the label to update/replace
*
* Returns: new label that is up to date
* else NULL on failure
* @request: requested perms
* @deny: Returns: explicit deny set
* @sa: initialized audit structure (MAY BE NULL if not auditing)
- * @cb: callback fn for tpye specific fields (MAY BE NULL)
+ * @cb: callback fn for type specific fields (MAY BE NULL)
*
* Returns: 0 if permission else error code
*
#include "include/policy_ns.h"
#include "include/procattr.h"
#include "include/mount.h"
+#include "include/secid.h"
/* Flag indicating whether initialization completed */
int apparmor_initialized;
tracer = begin_current_label_crit_section();
tracee = aa_get_task_label(child);
error = aa_may_ptrace(tracer, tracee,
- mode == PTRACE_MODE_READ ? AA_PTRACE_READ : AA_PTRACE_TRACE);
+ (mode & PTRACE_MODE_READ) ? AA_PTRACE_READ
+ : AA_PTRACE_TRACE);
aa_put_label(tracee);
end_current_label_crit_section(tracer);
return;
}
+static void apparmor_task_getsecid(struct task_struct *p, u32 *secid)
+{
+ struct aa_label *label = aa_get_task_label(p);
+ *secid = label->secid;
+ aa_put_label(label);
+}
+
static int apparmor_task_setrlimit(struct task_struct *task,
unsigned int resource, struct rlimit *new_rlim)
{
LSM_HOOK_INIT(task_free, apparmor_task_free),
LSM_HOOK_INIT(task_alloc, apparmor_task_alloc),
+ LSM_HOOK_INIT(task_getsecid, apparmor_task_getsecid),
LSM_HOOK_INIT(task_setrlimit, apparmor_task_setrlimit),
LSM_HOOK_INIT(task_kill, apparmor_task_kill),
+
+#ifdef CONFIG_AUDIT
+ LSM_HOOK_INIT(audit_rule_init, aa_audit_rule_init),
+ LSM_HOOK_INIT(audit_rule_known, aa_audit_rule_known),
+ LSM_HOOK_INIT(audit_rule_match, aa_audit_rule_match),
+ LSM_HOOK_INIT(audit_rule_free, aa_audit_rule_free),
+#endif
+
+ LSM_HOOK_INIT(secid_to_secctx, apparmor_secid_to_secctx),
+ LSM_HOOK_INIT(secctx_to_secid, apparmor_secctx_to_secid),
+ LSM_HOOK_INIT(release_secctx, apparmor_release_secctx),
};
/*
if (apparmor_initialized && !policy_admin_capable(NULL))
return -EPERM;
- for (i = 0; i < AUDIT_MAX_INDEX; i++) {
- if (strcmp(val, audit_mode_names[i]) == 0) {
- aa_g_audit = i;
- return 0;
- }
- }
+ i = match_string(audit_mode_names, AUDIT_MAX_INDEX, val);
+ if (i < 0)
+ return -EINVAL;
- return -EINVAL;
+ aa_g_audit = i;
+ return 0;
}
static int param_get_mode(char *buffer, const struct kernel_param *kp)
if (apparmor_initialized && !policy_admin_capable(NULL))
return -EPERM;
- for (i = 0; i < APPARMOR_MODE_NAMES_MAX_INDEX; i++) {
- if (strcmp(val, aa_profile_mode_names[i]) == 0) {
- aa_g_profile_mode = i;
- return 0;
- }
- }
+ i = match_string(aa_profile_mode_names, APPARMOR_MODE_NAMES_MAX_INDEX,
+ val);
+ if (i < 0)
+ return -EINVAL;
- return -EINVAL;
+ aa_g_profile_mode = i;
+ return 0;
}
/*
return 0;
}
+ aa_secids_init();
+
error = aa_setup_dfa_engine();
if (error) {
AA_ERROR("Unable to setup dfa engine\n");
/**
* aa_dfa_next - step one character to the next state in the dfa
- * @dfa: the dfa to tranverse (NOT NULL)
+ * @dfa: the dfa to traverse (NOT NULL)
* @state: the state to start in
* @c: the input character to transition on
*
* @src_name: src_name of object being mediated (MAYBE_NULL)
* @type: type of filesystem (MAYBE_NULL)
* @trans: name of trans (MAYBE NULL)
- * @flags: filesystem idependent mount flags
+ * @flags: filesystem independent mount flags
* @data: filesystem mount flags
* @request: permissions requested
* @perms: the permissions computed for the request (NOT NULL)
if (!aa_policy_init(&profile->base, NULL, hname, gfp))
goto fail;
- if (!aa_label_init(&profile->label, 1))
+ if (!aa_label_init(&profile->label, 1, gfp))
goto fail;
/* update being set needed by fs interface */
audit_policy(label, op, ns_name, ent->new->base.hname,
"same as current profile, skipping",
error);
+ /* break refcount cycle with proxy. */
+ aa_put_proxy(ent->new->label.proxy);
+ ent->new->label.proxy = NULL;
goto skip;
}
* Remove a profile or sub namespace from the current namespace, so that
* they can not be found anymore and mark them as replaced by unconfined
*
- * NOTE: removing confinement does not restore rlimits to preconfinemnet values
+ * NOTE: removing confinement does not restore rlimits to preconfinement values
*
* Returns: size of data consume else error code if fails
*/
*/
if (label != peer &&
- !aa_capable(label, CAP_SYS_RESOURCE, SECURITY_CAP_NOAUDIT))
+ aa_capable(label, CAP_SYS_RESOURCE, SECURITY_CAP_NOAUDIT) != 0)
error = fn_for_each(label, profile,
audit_resource(profile, resource,
new_rlim->rlim_max, peer,
*
* This file contains AppArmor security identifier (secid) manipulation fns
*
- * Copyright 2009-2010 Canonical Ltd.
+ * Copyright 2009-2017 Canonical Ltd.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* License.
*
*
- * AppArmor allocates a unique secid for every profile loaded. If a profile
- * is replaced it receives the secid of the profile it is replacing.
- *
- * The secid value of 0 is invalid.
+ * AppArmor allocates a unique secid for every label used. If a label
+ * is replaced it receives the secid of the label it is replacing.
*/
-#include <linux/spinlock.h>
#include <linux/errno.h>
#include <linux/err.h>
+#include <linux/gfp.h>
+#include <linux/idr.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include "include/cred.h"
+#include "include/lib.h"
#include "include/secid.h"
+#include "include/label.h"
+#include "include/policy_ns.h"
+
+/*
+ * secids - do not pin labels with a refcount. They rely on the label
+ * properly updating/freeing them
+ */
-/* global counter from which secids are allocated */
-static u32 global_secid;
+#define AA_FIRST_SECID 1
+
+static DEFINE_IDR(aa_secids);
static DEFINE_SPINLOCK(secid_lock);
-/* TODO FIXME: add secid to profile mapping, and secid recycling */
+/*
+ * TODO: allow policy to reserve a secid range?
+ * TODO: add secid pinning
+ * TODO: use secid_update in label replace
+ */
+
+/**
+ * aa_secid_update - update a secid mapping to a new label
+ * @secid: secid to update
+ * @label: label the secid will now map to
+ */
+void aa_secid_update(u32 secid, struct aa_label *label)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&secid_lock, flags);
+ idr_replace(&aa_secids, label, secid);
+ spin_unlock_irqrestore(&secid_lock, flags);
+}
+
+/**
+ *
+ * see label for inverse aa_label_to_secid
+ */
+struct aa_label *aa_secid_to_label(u32 secid)
+{
+ struct aa_label *label;
+
+ rcu_read_lock();
+ label = idr_find(&aa_secids, secid);
+ rcu_read_unlock();
+
+ return label;
+}
+
+int apparmor_secid_to_secctx(u32 secid, char **secdata, u32 *seclen)
+{
+ /* TODO: cache secctx and ref count so we don't have to recreate */
+ struct aa_label *label = aa_secid_to_label(secid);
+ int len;
+
+ AA_BUG(!secdata);
+ AA_BUG(!seclen);
+
+ if (!label)
+ return -EINVAL;
+
+ if (secdata)
+ len = aa_label_asxprint(secdata, root_ns, label,
+ FLAG_SHOW_MODE | FLAG_VIEW_SUBNS |
+ FLAG_HIDDEN_UNCONFINED | FLAG_ABS_ROOT,
+ GFP_ATOMIC);
+ else
+ len = aa_label_snxprint(NULL, 0, root_ns, label,
+ FLAG_SHOW_MODE | FLAG_VIEW_SUBNS |
+ FLAG_HIDDEN_UNCONFINED | FLAG_ABS_ROOT);
+ if (len < 0)
+ return -ENOMEM;
+
+ *seclen = len;
+
+ return 0;
+}
+
+int apparmor_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid)
+{
+ struct aa_label *label;
+
+ label = aa_label_strn_parse(&root_ns->unconfined->label, secdata,
+ seclen, GFP_KERNEL, false, false);
+ if (IS_ERR(label))
+ return PTR_ERR(label);
+ *secid = label->secid;
+
+ return 0;
+}
+
+void apparmor_release_secctx(char *secdata, u32 seclen)
+{
+ kfree(secdata);
+}
/**
* aa_alloc_secid - allocate a new secid for a profile
+ * @label: the label to allocate a secid for
+ * @gfp: memory allocation flags
+ *
+ * Returns: 0 with @label->secid initialized
+ * <0 returns error with @label->secid set to AA_SECID_INVALID
*/
-u32 aa_alloc_secid(void)
+int aa_alloc_secid(struct aa_label *label, gfp_t gfp)
{
- u32 secid;
+ unsigned long flags;
+ int ret;
+
+ idr_preload(gfp);
+ spin_lock_irqsave(&secid_lock, flags);
+ ret = idr_alloc(&aa_secids, label, AA_FIRST_SECID, 0, GFP_ATOMIC);
+ spin_unlock_irqrestore(&secid_lock, flags);
+ idr_preload_end();
- /*
- * TODO FIXME: secid recycling - part of profile mapping table
- */
- spin_lock(&secid_lock);
- secid = (++global_secid);
- spin_unlock(&secid_lock);
- return secid;
+ if (ret < 0) {
+ label->secid = AA_SECID_INVALID;
+ return ret;
+ }
+
+ AA_BUG(ret == AA_SECID_INVALID);
+ label->secid = ret;
+ return 0;
}
/**
*/
void aa_free_secid(u32 secid)
{
- ; /* NOP ATM */
+ unsigned long flags;
+
+ spin_lock_irqsave(&secid_lock, flags);
+ idr_remove(&aa_secids, secid);
+ spin_unlock_irqrestore(&secid_lock, flags);
+}
+
+void aa_secids_init(void)
+{
+ idr_init_base(&aa_secids, AA_FIRST_SECID);
}
* This is one of the three key functions for hierarchy implementation.
* This function is responsible for re-evaluating all the cgroup's active
* exceptions due to a parent's exception change.
- * Refer to Documentation/cgroups/devices.txt for more details.
+ * Refer to Documentation/cgroup-v1/devices.txt for more details.
*/
static void revalidate_active_exceptions(struct dev_cgroup *devcg)
{
}
/* This supports connect(2) and SCTP connect services such as sctp_connectx(3)
- * and sctp_sendmsg(3) as described in Documentation/security/LSM-sctp.txt
+ * and sctp_sendmsg(3) as described in Documentation/security/LSM-sctp.rst
*/
static int selinux_socket_connect_helper(struct socket *sock,
struct sockaddr *address, int addrlen)
depends on SND_OSSEMUL
help
To enable OSS mixer API emulation (/dev/mixer*), say Y here
- and read <file:Documentation/sound/alsa/OSS-Emulation.txt>.
+ and read <file:Documentation/sound/designs/oss-emulation.rst>.
Many programs still use the OSS API, so say Y.
select SND_PCM
help
To enable OSS digital audio (PCM) emulation (/dev/dsp*), say Y
- here and read <file:Documentation/sound/alsa/OSS-Emulation.txt>.
+ here and read <file:Documentation/sound/designs/oss-emulation.rst>.
Many programs still use the OSS API, so say Y.
select SND_RAWMIDI
help
To include support for MIDI serial port interfaces, say Y here
- and read <file:Documentation/sound/alsa/serial-u16550.txt>.
+ and read <file:Documentation/sound/cards/serial-u16550.rst>.
This driver works with serial UARTs 16550 and better.
This driver accesses the serial port hardware directly, so
the device frequently. A value of 10 seconds would be a
good choice for normal operations.
- See Documentation/sound/alsa/powersave.txt for more details.
+ See Documentation/sound/designs/powersave.rst for more details.
config SND_AC97_POWER_SAVE_DEFAULT
int "Default time-out for AC97 power-save mode"
help
If you want to record audio from TV cards based on
Brooktree Bt878/Bt879 chips, say Y here and read
- <file:Documentation/sound/alsa/Bt87x.txt>.
+ <file:Documentation/sound/cards/bt87x.rst>.
To compile this driver as a module, choose M here: the module
will be called snd-bt87x.
help
If you want to use soundcards based on C-Media CMI8338, CMI8738,
CMI8768 or CMI8770 chips, say Y here and read
- <file:Documentation/sound/alsa/CMIPCI.txt>.
+ <file:Documentation/sound/cards/cmipci.rst>.
To compile this driver as a module, choose M here: the module
will be called snd-cmipci.
Audigy and E-mu APS (partially supported) soundcards.
The confusing multitude of mixer controls is documented in
- <file:Documentation/sound/alsa/SB-Live-mixer.txt> and
- <file:Documentation/sound/alsa/Audigy-mixer.txt>.
+ <file:Documentation/sound/cards/sb-live-mixer.rst> and
+ <file:Documentation/sound/cards/audigy-mixer.rst>.
To compile this driver as a module, choose M here: the module
will be called snd-emu10k1.
select SND_PCM
help
If you want to use Digigram miXart soundcards, say Y here and
- read <file:Documentation/sound/alsa/MIXART.txt>.
+ read <file:Documentation/sound/cards/mixart.rst>.
To compile this driver as a module, choose M here: the module
will be called snd-mixart.
if (!kctl)
return -ENOMEM;
kctl->id.device = device;
- snd_ctl_add(emu->card, kctl);
+ err = snd_ctl_add(emu->card, kctl);
+ if (err < 0)
+ return err;
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(emu->pci), 64*1024, 64*1024);
if ((err = snd_ac97_mixer(chip->ac97_bus, &ac97, &chip->ac97_sec)) < 0)
return err;
}
- for (i = 0; i < FM801_CONTROLS; i++)
- snd_ctl_add(chip->card, snd_ctl_new1(&snd_fm801_controls[i], chip));
+ for (i = 0; i < FM801_CONTROLS; i++) {
+ err = snd_ctl_add(chip->card,
+ snd_ctl_new1(&snd_fm801_controls[i], chip));
+ if (err < 0)
+ return err;
+ }
if (chip->multichannel) {
- for (i = 0; i < FM801_CONTROLS_MULTI; i++)
- snd_ctl_add(chip->card, snd_ctl_new1(&snd_fm801_controls_multi[i], chip));
+ for (i = 0; i < FM801_CONTROLS_MULTI; i++) {
+ err = snd_ctl_add(chip->card,
+ snd_ctl_new1(&snd_fm801_controls_multi[i], chip));
+ if (err < 0)
+ return err;
+ }
}
return 0;
}
SND_PCI_QUIRK(0x103c, 0x8079, "HP EliteBook 840 G3", CXT_FIXUP_HP_DOCK),
SND_PCI_QUIRK(0x103c, 0x807C, "HP EliteBook 820 G3", CXT_FIXUP_HP_DOCK),
SND_PCI_QUIRK(0x103c, 0x80FD, "HP ProBook 640 G2", CXT_FIXUP_HP_DOCK),
+ SND_PCI_QUIRK(0x103c, 0x83b3, "HP EliteBook 830 G5", CXT_FIXUP_HP_DOCK),
+ SND_PCI_QUIRK(0x103c, 0x83d3, "HP ProBook 640 G4", CXT_FIXUP_HP_DOCK),
SND_PCI_QUIRK(0x103c, 0x8174, "HP Spectre x360", CXT_FIXUP_HP_SPECTRE),
SND_PCI_QUIRK(0x103c, 0x8115, "HP Z1 Gen3", CXT_FIXUP_HP_GATE_MIC),
SND_PCI_QUIRK(0x103c, 0x814f, "HP ZBook 15u G3", CXT_FIXUP_MUTE_LED_GPIO),
{
struct alc_spec *spec = codec->spec;
+ if (!snd_hda_get_bool_hint(codec, "shutup"))
+ return; /* disabled explicitly by hints */
+
if (spec && spec->shutup)
spec->shutup(codec);
else
/* dsp port */
chip->port_dsp_bar = pci_ioremap_bar(pci, 2);
+ if (!chip->port_dsp_bar) {
+ dev_err(card->dev, "cannot remap PCI memory region\n");
+ goto remap_pci_failed;
+ }
err = request_threaded_irq(pci->irq, lx_interrupt, lx_threaded_irq,
IRQF_SHARED, KBUILD_MODNAME, chip);
free_irq(pci->irq, chip);
request_irq_failed:
+ iounmap(chip->port_dsp_bar);
+
+remap_pci_failed:
pci_release_regions(pci);
request_regions_failed:
static int snd_sonicvibes_create_gameport(struct sonicvibes *sonic)
{
struct gameport *gp;
+ int err;
sonic->gameport = gp = gameport_allocate_port();
if (!gp) {
gameport_register_port(gp);
- snd_ctl_add(sonic->card, snd_ctl_new1(&snd_sonicvibes_game_control, sonic));
+ err = snd_ctl_add(sonic->card,
+ snd_ctl_new1(&snd_sonicvibes_game_control, sonic));
+ if (err < 0)
+ return err;
return 0;
}
return err;
}
- snd_sonicvibes_create_gameport(sonic);
+ err = snd_sonicvibes_create_gameport(sonic);
+ if (err < 0) {
+ snd_card_free(card);
+ return err;
+ }
if ((err = snd_card_register(card)) < 0) {
snd_card_free(card);
struct snd_pcm_chmap_elem *chmap; /* (optional) channel map */
bool dsd_dop; /* add DOP headers in case of DSD samples */
bool dsd_bitrev; /* reverse the bits of each DSD sample */
+ bool dsd_raw; /* altsetting is raw DSD */
};
struct snd_usb_substream;
sample_width = fmt->bBitResolution;
sample_bytes = fmt->bSubslotSize;
- if (format & UAC2_FORMAT_TYPE_I_RAW_DATA)
+ if (format & UAC2_FORMAT_TYPE_I_RAW_DATA) {
pcm_formats |= SNDRV_PCM_FMTBIT_SPECIAL;
+ /* flag potentially raw DSD capable altsettings */
+ fp->dsd_raw = true;
+ }
format <<= 1;
break;
NULL, NULL, unitid, 0, 0);
}
-static void get_connector_control_name(struct mixer_build *state,
+static void get_connector_control_name(struct usb_mixer_interface *mixer,
struct usb_audio_term *term,
bool is_input, char *name, int name_size)
{
- int name_len = get_term_name(state->chip, term, name, name_size, 0);
+ int name_len = get_term_name(mixer->chip, term, name, name_size, 0);
if (name_len == 0)
strlcpy(name, "Unknown", name_size);
}
/* Build a mixer control for a UAC connector control (jack-detect) */
-static void build_connector_control(struct mixer_build *state,
+static void build_connector_control(struct usb_mixer_interface *mixer,
struct usb_audio_term *term, bool is_input)
{
struct snd_kcontrol *kctl;
cval = kzalloc(sizeof(*cval), GFP_KERNEL);
if (!cval)
return;
- snd_usb_mixer_elem_init_std(&cval->head, state->mixer, term->id);
+ snd_usb_mixer_elem_init_std(&cval->head, mixer, term->id);
/*
* UAC2: The first byte from reading the UAC2_TE_CONNECTOR control returns the
* number of channels connected.
* This boolean ctl will simply report if any channels are connected
* or not.
*/
- if (state->mixer->protocol == UAC_VERSION_2)
+ if (mixer->protocol == UAC_VERSION_2)
cval->control = UAC2_TE_CONNECTOR;
else /* UAC_VERSION_3 */
cval->control = UAC3_TE_INSERTION;
cval->max = 1;
kctl = snd_ctl_new1(&usb_connector_ctl_ro, cval);
if (!kctl) {
- usb_audio_err(state->chip, "cannot malloc kcontrol\n");
+ usb_audio_err(mixer->chip, "cannot malloc kcontrol\n");
kfree(cval);
return;
}
- get_connector_control_name(state, term, is_input, kctl->id.name,
+ get_connector_control_name(mixer, term, is_input, kctl->id.name,
sizeof(kctl->id.name));
kctl->private_free = snd_usb_mixer_elem_free;
snd_usb_mixer_add_control(&cval->head, kctl);
/* Check for jack detection. */
if (uac_v2v3_control_is_readable(bmctls, control))
- build_connector_control(state, &iterm, true);
+ build_connector_control(state->mixer, &iterm, true);
return 0;
}
UAC3_BADD_FU_ID7, map->map);
}
+ /* Insertion Control */
+ if (f->subclass == UAC3_FUNCTION_SUBCLASS_HEADSET_ADAPTER) {
+ struct usb_audio_term iterm, oterm;
+
+ /* Input Term - Insertion control */
+ memset(&iterm, 0, sizeof(iterm));
+ iterm.id = UAC3_BADD_IT_ID4;
+ iterm.type = UAC_BIDIR_TERMINAL_HEADSET;
+ build_connector_control(mixer, &iterm, true);
+
+ /* Output Term - Insertion control */
+ memset(&oterm, 0, sizeof(oterm));
+ oterm.id = UAC3_BADD_OT_ID3;
+ oterm.type = UAC_BIDIR_TERMINAL_HEADSET;
+ build_connector_control(mixer, &oterm, false);
+ }
+
return 0;
}
if (uac_v2v3_control_is_readable(le16_to_cpu(desc->bmControls),
UAC2_TE_CONNECTOR)) {
- build_connector_control(&state, &state.oterm,
+ build_connector_control(state.mixer, &state.oterm,
false);
}
} else { /* UAC_VERSION_3 */
if (uac_v2v3_control_is_readable(le32_to_cpu(desc->bmControls),
UAC3_TE_INSERTION)) {
- build_connector_control(&state, &state.oterm,
+ build_connector_control(state.mixer, &state.oterm,
false);
}
}
err = snd_usb_mixer_controls(mixer);
if (err < 0)
goto _error;
- err = snd_usb_mixer_status_create(mixer);
- if (err < 0)
- goto _error;
}
+
+ err = snd_usb_mixer_status_create(mixer);
+ if (err < 0)
+ goto _error;
+
err = create_keep_iface_ctl(mixer);
if (err < 0)
goto _error;
}
},
+/* disabled due to regression for other devices;
+ * see https://bugzilla.kernel.org/show_bug.cgi?id=199905
+ */
+#if 0
{
/*
* Nura's first gen headphones use Cambridge Silicon Radio's vendor
}
}
},
+#endif /* disabled */
{
/*
case USB_ID(0x1511, 0x0037): /* AURALiC VEGA */
case USB_ID(0x20b1, 0x0002): /* Wyred 4 Sound DAC-2 DSD */
case USB_ID(0x20b1, 0x2004): /* Matrix Audio X-SPDIF 2 */
- case USB_ID(0x20b1, 0x3008): /* iFi Audio micro/nano iDSD */
case USB_ID(0x20b1, 0x2008): /* Matrix Audio X-Sabre */
case USB_ID(0x20b1, 0x300a): /* Matrix Audio Mini-i Pro */
case USB_ID(0x22d9, 0x0416): /* OPPO HA-1 */
case USB_ID(0x22d9, 0x0436): /* OPPO Sonica */
case USB_ID(0x22d9, 0x0461): /* OPPO UDP-205 */
case USB_ID(0x2522, 0x0012): /* LH Labs VI DAC Infinity */
- case USB_ID(0x25ce, 0x001f): /* Mytek Brooklyn DAC */
- case USB_ID(0x25ce, 0x0021): /* Mytek Manhattan DAC */
- case USB_ID(0x25ce, 0x8025): /* Mytek Brooklyn DAC+ */
case USB_ID(0x2772, 0x0230): /* Pro-Ject Pre Box S2 Digital */
if (fp->altsetting == 2)
return SNDRV_PCM_FMTBIT_DSD_U32_BE;
case USB_ID(0x20b1, 0x3021): /* Eastern El. MiniMax Tube DAC Supreme */
case USB_ID(0x20b1, 0x3023): /* Aune X1S 32BIT/384 DSD DAC */
case USB_ID(0x20b1, 0x302d): /* Unison Research Unico CD Due */
- case USB_ID(0x20b1, 0x3036): /* Holo Springs Level 3 R2R DAC */
case USB_ID(0x20b1, 0x307b): /* CH Precision C1 DAC */
case USB_ID(0x20b1, 0x3086): /* Singxer F-1 converter board */
case USB_ID(0x22d9, 0x0426): /* OPPO HA-2 */
return SNDRV_PCM_FMTBIT_DSD_U32_BE;
}
+ /* Mostly generic method to detect many DSD-capable implementations -
+ * from XMOS/Thesycon
+ */
+ switch (USB_ID_VENDOR(chip->usb_id)) {
+ case 0x20b1: /* XMOS based devices */
+ case 0x25ce: /* Mytek devices */
+ if (fp->dsd_raw)
+ return SNDRV_PCM_FMTBIT_DSD_U32_BE;
+ break;
+ default:
+ break;
+
+ }
+
return 0;
}
* asking selinux for a specific new context (e.g. with runcon) will result
* in execve returning -EPERM.
*
- * See Documentation/prctl/no_new_privs.txt for more details.
+ * See Documentation/userspace-api/no_new_privs.rst for more details.
*/
#define PR_SET_NO_NEW_PRIVS 38
#define PR_GET_NO_NEW_PRIVS 39
/*
* Check for "NAME_PATH" environment variable to override fs location (for
- * testing). This matches the recommendation in Documentation/sysfs-rules.txt
+ * testing). This matches the recommendation in Documentation/admin-guide/sysfs-rules.rst
* for SYSFS_PATH.
*/
static bool fs__env_override(struct fs *fs)
/*
* Convert type string (u8/u16/u32/u64/s8/s16/s32/s64 ..., see
- * Documentation/trace/kprobetrace.txt) to size field of BPF_LDX_MEM
+ * Documentation/trace/kprobetrace.rst) to size field of BPF_LDX_MEM
* instruction (BPF_{B,H,W,DW}).
*/
static int
# example: [color=#CC00CC]
#
# arglist: A list of arguments from registers/stack addresses. See URL:
-# https://www.kernel.org/doc/Documentation/trace/kprobetrace.txt
+# https://www.kernel.org/doc/Documentation/trace/kprobetrace.rst
#
# example: cpu=%di:s32
#
# example: [color=#CC00CC]
#
# arglist: A list of arguments from registers/stack addresses. See URL:
-# https://www.kernel.org/doc/Documentation/trace/kprobetrace.txt
+# https://www.kernel.org/doc/Documentation/trace/kprobetrace.rst
#
# example: port=+36(%di):s32
#
TEST_CUSTOM_PROGS = $(OUTPUT)/urandom_read
all: $(TEST_CUSTOM_PROGS)
-$(TEST_CUSTOM_PROGS): urandom_read
-
-urandom_read: urandom_read.c
+$(TEST_CUSTOM_PROGS): $(OUTPUT)/%: %.c
$(CC) -o $(TEST_CUSTOM_PROGS) -static $< -Wl,--build-id
# Order correspond to 'make run_tests' order
return len;
}
-static ssize_t write_text(const char *path, char *buf, size_t len)
+static ssize_t write_text(const char *path, char *buf, ssize_t len)
{
int fd;
int cg_write(const char *cgroup, const char *control, char *buf)
{
char path[PATH_MAX];
- size_t len = strlen(buf);
+ ssize_t len = strlen(buf);
snprintf(path, sizeof(path), "%s/%s", cgroup, control);
ARG2=%r1
OFFS=4
;;
+ppc64*)
+ ARG2=%r4
+ OFFS=8
+;;
+ppc*)
+ ARG2=%r4
+ OFFS=4
+;;
*)
echo "Please implement other architecture here"
exit_untested
GOODREG=%r0
BADREG=%ax
;;
+ppc*)
+ GOODREG=%r3
+ BADREG=%msr
+;;
+*)
+ echo "Please implement other architecture here"
+ exit_untested
esac
test_goodarg() # Good-args
"matchPattern": "action order [0-9]*: ife encode action pass.*type 0xED3E.*use tcindex 65535.*index 1",
"matchCount": "1",
"teardown": [
- "$TC actions flush action skbedit"
+ "$TC actions flush action ife"
]
},
{