On Mon, Jan 21, 2019 at 03:28:39PM +0100, Wolfram Sang wrote: > Here is a fault injector simulating 'arbitration lost' from multi-master > setups. Read the docs for its usage. > > Signed-off-by: Wolfram Sang <wsa+renesas@xxxxxxxxxxxxxxxxxxxx> Geert, if you would have time for a high-level review, I'd appreciate this very much! > --- > > This is the most reliable result I came up with so far for simulating lost > arbitration (after playing a lot with falling SDA as trigger first, but SCL > seems the way to go). Works fine with the i2c-sh_mobile driver on a Renesas > Lager board. I am not super-happy with the interrupt latency causing some bits > to be non-disturbed, but for now, I don't see a way around it except for > busy-polling which I think is too excessive. RFC for now because someone still > might have a better idea :) > > Needs my previous fault-injector cleanup patches, a branch for consuming is here: > > git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux.git renesas/iic-arbitration-lost > > Now let's see how to fix the sh_mobile driver... > > Documentation/i2c/gpio-fault-injection | 26 +++++++++++++++ > drivers/i2c/busses/i2c-gpio.c | 61 ++++++++++++++++++++++++++++++++++ > 2 files changed, 87 insertions(+) > > diff --git a/Documentation/i2c/gpio-fault-injection b/Documentation/i2c/gpio-fault-injection > index 1a44e3edc0c4..b6f36ffe55e1 100644 > --- a/Documentation/i2c/gpio-fault-injection > +++ b/Documentation/i2c/gpio-fault-injection > @@ -83,3 +83,29 @@ This is why bus recovery (up to 9 clock pulses) must either check SDA or send > additional STOP conditions to ensure the bus has been released. Otherwise > random data will be written to a device! > > +Lost arbitration > +================ > + > +Here, we want to simulate the condition where the master under tests loses the > +bus arbitration against another master in a multi-master setup. > + > +"lose_arbitration" > +------------------ > + > +This file is write only and you need to write the number of desired lost > +arbitrations in a row. The calling process will then sleep and interfere with > +transfers from the master under test when they appear until that number is > +reached. The process is interruptible, though. > + > +Arbitration lost is achieved by waiting for SCL going down by the master under > +test and then pulling SDA low for some time. So, the I2C address sent out > +should be corrupted and that should be detected properly. That means that the > +address sent out should have a lot of '1' bits to be able to detect corruption. > +There doesn't need to be a device at this address because arbitration lost > +should be detected beforehand. Also note, that SCL going down is monitored > +using interrupts, so the interrupt latency might cause the first bits to be not > +corrupted. A good starting script for using this fault injector: > + > +# echo 1 > lose_arbitration & > +# i2cget -y <bus_to_test> 0x3f > + > diff --git a/drivers/i2c/busses/i2c-gpio.c b/drivers/i2c/busses/i2c-gpio.c > index ca04fa25a141..c630172b4787 100644 > --- a/drivers/i2c/busses/i2c-gpio.c > +++ b/drivers/i2c/busses/i2c-gpio.c > @@ -7,12 +7,14 @@ > * it under the terms of the GNU General Public License version 2 as > * published by the Free Software Foundation. > */ > +#include <linux/completion.h> > #include <linux/debugfs.h> > #include <linux/delay.h> > #include <linux/gpio/consumer.h> > #include <linux/i2c-algo-bit.h> > #include <linux/i2c.h> > #include <linux/init.h> > +#include <linux/interrupt.h> > #include <linux/module.h> > #include <linux/of.h> > #include <linux/platform_data/i2c-gpio.h> > @@ -27,6 +29,7 @@ struct i2c_gpio_private_data { > struct i2c_gpio_platform_data pdata; > #ifdef CONFIG_I2C_GPIO_FAULT_INJECTOR > struct dentry *debug_dir; > + struct completion irq_happened; > #endif > }; > > @@ -162,6 +165,59 @@ static int fops_incomplete_write_byte_set(void *data, u64 addr) > } > DEFINE_DEBUGFS_ATTRIBUTE(fops_incomplete_write_byte, NULL, fops_incomplete_write_byte_set, "%llu\n"); > > +static irqreturn_t lose_arbitration_irq(int irq, void *dev_id) > +{ > + struct i2c_gpio_private_data *priv = dev_id; > + > + setsda(&priv->bit_data, 0); > + udelay(200); > + setsda(&priv->bit_data, 1); > + > + complete(&priv->irq_happened); > + return IRQ_HANDLED; > +} > + > +static int fops_lose_arbitration_set(void *data, u64 num_faults) > +{ > + struct i2c_gpio_private_data *priv = data; > + int irq = gpiod_to_irq(priv->scl); > + int ret, i; > + > + i2c_lock_bus(&priv->adap, I2C_LOCK_ROOT_ADAPTER); > + > + /* > + * Interrupt on falling SCL. This ensures that the master under test has > + * really started the transfer. Interrupt on falling SDA did only > + * exercise 'bus busy' detection on some HW but not 'arbitration lost'. > + * Note that the interrupt latency may cause the first bits to be > + * transmitted correctly. > + */ > + ret = gpiod_direction_input(priv->scl); > + if (ret) > + goto unlock; > + > + ret = request_irq(irq, lose_arbitration_irq, IRQF_TRIGGER_FALLING, > + "i2c-gpio-fi", priv); > + if (ret) > + goto output; > + > + for (i = 0; i < num_faults; i++) { > + ret = wait_for_completion_interruptible(&priv->irq_happened); > + if (ret) > + break; > + reinit_completion(&priv->irq_happened); > + } > + > + free_irq(irq, priv); > + output: > + ret = gpiod_direction_output(priv->scl, 1); > + unlock: > + i2c_unlock_bus(&priv->adap, I2C_LOCK_ROOT_ADAPTER); > + > + return ret; > +} > +DEFINE_DEBUGFS_ATTRIBUTE(fops_lose_arbitration, NULL, fops_lose_arbitration_set, "%llu\n"); > + > static void i2c_gpio_fault_injector_init(struct platform_device *pdev) > { > struct i2c_gpio_private_data *priv = platform_get_drvdata(pdev); > @@ -181,10 +237,15 @@ static void i2c_gpio_fault_injector_init(struct platform_device *pdev) > if (!priv->debug_dir) > return; > > + init_completion(&priv->irq_happened); > + > debugfs_create_file_unsafe("incomplete_address_phase", 0200, priv->debug_dir, > priv, &fops_incomplete_addr_phase); > debugfs_create_file_unsafe("incomplete_write_byte", 0200, priv->debug_dir, > priv, &fops_incomplete_write_byte); > + if (priv->bit_data.getscl) > + debugfs_create_file_unsafe("lose_arbitration", 0200, priv->debug_dir, > + priv, &fops_lose_arbitration); > debugfs_create_file_unsafe("scl", 0600, priv->debug_dir, priv, &fops_scl); > debugfs_create_file_unsafe("sda", 0600, priv->debug_dir, priv, &fops_sda); > } > -- > 2.11.0 >
Attachment:
signature.asc
Description: PGP signature