[Skiboot] [PATCH 1/4] i2c: Add i2c_run_req() to crank the state machine for a request

Stewart Smith stewart at linux.vnet.ibm.com
Thu Nov 24 17:13:09 AEDT 2016


Claudio Carvalho <cclaudio at linux.vnet.ibm.com> writes:
> On 11/23/2016 03:45 AM, Stewart Smith wrote:
>> Doing everything asynchronously is brilliant, it's exactly what we
>> want to do.
>> 
>> Except... the tpm driver wants to do things synchronously, which isn't
>> so cool.
>> 
>> For reasons that are not yet completely known, we spend an awful lot of
>> time in the main thread *not* running pollers (potentially seconds), which
>> doesn't bode well for I2C timeouts.
>> 
>> Since the TPM measure is done in a secondary thread, we do *not* run pollers
>> there either (as of 323c8aeb54bd4e0b9004091fcbb4a9daeda2f576 - which is
>> roughly as of skiboot 2.1.1).
>> 
>> But we still need to crank the i2c state machine, so we introduce a call
>> to do just that. It will return how long the poll interval should be, so
>> that we can time_wait() for a more appropriate time for whatever i2c
>> implementation is sitting behind things.
>> 
>> Without this, it was "easy" to get to a situation where the i2c state machine
>> wasn't cranked at all, and you'd hit the i2c timeout (for the issued operation)
>> before the poller to crank i2c was ever called.
>> 
>> Signed-off-by: Stewart Smith <stewart at linux.vnet.ibm.com>
>> ---
>>  hw/p8-i2c.c                        | 18 ++++++++++++++++++
>>  include/i2c.h                      |  8 ++++++++
>>  libstb/drivers/tpm_i2c_interface.c | 14 +++++++++-----
>>  3 files changed, 35 insertions(+), 5 deletions(-)
>> 
>> diff --git a/hw/p8-i2c.c b/hw/p8-i2c.c
>> index 4e29432..12a4d1a 100644
>> --- a/hw/p8-i2c.c
>> +++ b/hw/p8-i2c.c
>> @@ -1064,6 +1064,23 @@ static void p8_i2c_set_request_timeout(struct i2c_request *req,
>>  	request->timeout = msecs_to_tb(duration);
>>  }
>>  
>> +static uint64_t p8_i2c_run_request(struct i2c_request *req)
>> +{
>> +	struct i2c_bus *bus = req->bus;
>> +	struct p8_i2c_master_port *port =
>> +		container_of(bus, struct p8_i2c_master_port, bus);
>> +	struct p8_i2c_master *master = port->master;
>> +	uint64_t poll_interval = 0;
>> +
>> +	lock(&master->lock);
>> +	p8_i2c_check_status(master);
>> +	p8_i2c_check_work(master);
>> +	poll_interval = master->poll_interval;
>> +	unlock(&master->lock);
>> +
>> +	return poll_interval;
>> +}
>> +
>>  static inline uint32_t p8_i2c_get_bit_rate_divisor(uint32_t lb_freq,
>>  						   uint32_t bus_speed)
>>  {
>> @@ -1399,6 +1416,7 @@ static void p8_i2c_init_one(struct dt_node *i2cm, enum p8_i2c_master_type type)
>>  		port->bus.alloc_req = p8_i2c_alloc_request;
>>  		port->bus.free_req = p8_i2c_free_request;
>>  		port->bus.set_req_timeout = p8_i2c_set_request_timeout;
>> +		port->bus.run_req = p8_i2c_run_request;
>>  		i2c_add_bus(&port->bus);
>>  
>>  		/* Add OPAL properties to the bus node */
>> diff --git a/include/i2c.h b/include/i2c.h
>> index 83c6ec5..a10a179 100644
>> --- a/include/i2c.h
>> +++ b/include/i2c.h
>> @@ -28,6 +28,7 @@ struct i2c_bus {
>>  	void			(*free_req)(struct i2c_request *req);
>>  	void			(*set_req_timeout)(struct i2c_request *req,
>>  						   uint64_t duration);
>> +	uint64_t		(*run_req)(struct i2c_request *req);
>>  };
>>  
>>  /*
>> @@ -89,6 +90,13 @@ static inline void i2c_set_req_timeout(struct i2c_request *req,
>>  		req->bus->set_req_timeout(req, duration);
>>  }
>>  
>> +static inline uint64_t i2c_run_req(struct i2c_request *req)
>> +{
>> +	if (req->bus->run_req)
>> +		return req->bus->run_req(req);
>> +	return 0;
>> +}
>> +
>>  /* P8 implementation details */
>>  extern void p8_i2c_init(void);
>>  extern void p8_i2c_interrupt(uint32_t chip_id);
>> diff --git a/libstb/drivers/tpm_i2c_interface.c b/libstb/drivers/tpm_i2c_interface.c
>> index ba0c5a8..4d136fd 100644
>> --- a/libstb/drivers/tpm_i2c_interface.c
>> +++ b/libstb/drivers/tpm_i2c_interface.c
>> @@ -57,6 +57,7 @@ int tpm_i2c_request_send(int tpm_bus_id, int tpm_dev_addr, int read_write,
>>  	int rc, waited, retries, timeout;
>>  	struct i2c_request *req;
>>  	struct i2c_bus *bus;
>> +	uint64_t time_to_wait = 0;
>>  
>>  	bus = i2c_find_bus_by_id(tpm_bus_id);
>>  	if (!bus) {
>> @@ -106,9 +107,12 @@ int tpm_i2c_request_send(int tpm_bus_id, int tpm_dev_addr, int read_write,
>>  		i2c_queue_req(req);
>>  
>>  		do {
>> -			time_wait_ms(REQ_COMPLETE_POLLING);
>> -			waited += REQ_COMPLETE_POLLING;
>> -		} while (waited < timeout && rc == 1);
>> +			time_to_wait = i2c_run_req(req);
>> +			if (!time_to_wait)
>> +				time_to_wait = REQ_COMPLETE_POLLING;
>> +			time_wait(time_to_wait);
>> +			waited += time_to_wait;
>> +		} while (tb_to_msecs(waited) < timeout && rc == 1);
>
> With this we will be checking if the request is complete either on a
> master->poll_interval or REQ_COMPLETE_POLLING interval. Additionally,
> i2c_run_req() forces the request status to be updated on every polling
> interval.
>
> Do we need to force the request status to be updated on every polling
> interval?
>
> If we have something like below, we may end up checking the status
> REQ_COMPLETE_POLLING msec after it was updated. Is that what you are
> trying avoid here? rc seems to be updated even if we are running on a
> secondary thread. Maybe I don't fully understand the problem explained
> in the commit message.
>
> do {
> 	time_wait_ms(REQ_COMPLETE_POLLING);
> } while (rc == 1);

I think for the most part that if it is getting serviced, you're just
being lucky with timing - and this isn't always necessarily the case.


-- 
Stewart Smith
OPAL Architect, IBM.



More information about the Skiboot mailing list