RE: [PATCH 3/4] ACPI / EC: Fix a gap that ECDT EC cannot handle EC events

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi, Peter

> From: Peter Wu [mailto:peter@xxxxxxxxxxxxx]
> Subject: Re: [PATCH 3/4] ACPI / EC: Fix a gap that ECDT EC cannot handle EC events
> 
> On Fri, Sep 02, 2016 at 03:46:46PM +0800, Lv Zheng wrote:
> > It is possible to register _Qxx from namespace and use the ECDT EC to
> > perform event handling. The reported bug reveals that Windows is using ECDT
> > in this way in case the namespace EC is not present. This patch facilitates
> > Linux to support ECDT in this way.
> >
> > Link: https://bugzilla.kernel.org/show_bug.cgi?id=115021
> > Reported-and-tested-by: Luya Tshimbalanga <luya@xxxxxxxxxxxxxxxxx>
> > Suggested-by: Peter Wu <peter@xxxxxxxxxxxxx>
> > Cc: Luya Tshimbalanga <luya@xxxxxxxxxxxxxxxxx>
> > Cc: Peter Wu <peter@xxxxxxxxxxxxx>
> > Signed-off-by: Lv Zheng <lv.zheng@xxxxxxxxx>
> > ---
> >  drivers/acpi/ec.c       |  119 ++++++++++++++++++++++++++++++++++++++---------
> >  drivers/acpi/internal.h |    1 +
> >  drivers/acpi/scan.c     |    1 +
> >  3 files changed, 98 insertions(+), 23 deletions(-)
> >
> > diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c
> > index 4b4c0cb..847e665 100644
> > --- a/drivers/acpi/ec.c
> > +++ b/drivers/acpi/ec.c
> > @@ -108,6 +108,7 @@ enum {
> >  	EC_FLAGS_QUERY_GUARDING,	/* Guard for SCI_EVT check */
> >  	EC_FLAGS_GPE_HANDLER_INSTALLED,	/* GPE handler installed */
> >  	EC_FLAGS_EC_HANDLER_INSTALLED,	/* OpReg handler installed */
> > +	EC_FLAGS_EVT_HANDLER_INSTALLED, /* _Qxx handlers installed */
> >  	EC_FLAGS_STARTED,		/* Driver is started */
> >  	EC_FLAGS_STOPPED,		/* Driver is stopped */
> >  	EC_FLAGS_COMMAND_STORM,		/* GPE storms occurred to the
> > @@ -1305,7 +1306,7 @@ ec_parse_device(acpi_handle handle, u32 Level, void *context, void **retval)
> >   *       handler is not installed, which means "not able to handle
> >   *       transactions".
> >   */
> > -static int ec_install_handlers(struct acpi_ec *ec)
> > +static int ec_install_handlers(struct acpi_ec *ec, bool handle_events)
> >  {
> >  	acpi_status status;
> >
> > @@ -1334,6 +1335,16 @@ static int ec_install_handlers(struct acpi_ec *ec)
> >  		set_bit(EC_FLAGS_EC_HANDLER_INSTALLED, &ec->flags);
> >  	}
> >
> > +	if (!handle_events)
> > +		return 0;
> > +
> > +	if (!test_bit(EC_FLAGS_EVT_HANDLER_INSTALLED, &ec->flags)) {
> > +		/* Find and register all query methods */
> > +		acpi_walk_namespace(ACPI_TYPE_METHOD, ec->handle, 1,
> > +				    acpi_ec_register_query_methods,
> > +				    NULL, ec, NULL);
> > +		set_bit(EC_FLAGS_EVT_HANDLER_INSTALLED, &ec->flags);
> > +	}
> >  	if (!test_bit(EC_FLAGS_GPE_HANDLER_INSTALLED, &ec->flags)) {
> >  		status = acpi_install_gpe_raw_handler(NULL, ec->gpe,
> >  					  ACPI_GPE_EDGE_TRIGGERED,
> > @@ -1344,6 +1355,9 @@ static int ec_install_handlers(struct acpi_ec *ec)
> >  			if (test_bit(EC_FLAGS_STARTED, &ec->flags) &&
> >  			    ec->reference_count >= 1)
> >  				acpi_ec_enable_gpe(ec, true);
> > +
> > +			/* EC is fully operational, allow queries */
> > +			clear_bit(EC_FLAGS_QUERY_PENDING, &ec->flags);
> 
> It makes sense to allow queries only if it can receive GPEs for such
> events. You do not set this flag again in acpi_ec_remove_query_handlers
> because the EC is destroyed and not re-used right?

This is a hidden logic in old code.
It's corrected in the upstream linux-pm.git.
When this patch is re-sent, the code here could probably be replaced by acpi_ec_enable_event()/acpi_ec_disable_event().

> 
> >  		}
> >  	}
> >
> > @@ -1378,13 +1392,17 @@ static void ec_remove_handlers(struct acpi_ec *ec)
> >  			pr_err("failed to remove gpe handler\n");
> >  		clear_bit(EC_FLAGS_GPE_HANDLER_INSTALLED, &ec->flags);
> >  	}
> > +	if (test_bit(EC_FLAGS_EVT_HANDLER_INSTALLED, &ec->flags)) {
> > +		acpi_ec_remove_query_handlers(ec, true, 0);
> > +		clear_bit(EC_FLAGS_EVT_HANDLER_INSTALLED, &ec->flags);
> > +	}
> >  }
> >
> > -static int acpi_ec_setup(struct acpi_ec *ec)
> > +static int acpi_ec_setup(struct acpi_ec *ec, bool handle_events)
> >  {
> >  	int ret;
> >
> > -	ret = ec_install_handlers(ec);
> > +	ret = ec_install_handlers(ec, handle_events);
> >  	if (ret)
> >  		return ret;
> >
> > @@ -1400,18 +1418,33 @@ static int acpi_ec_setup(struct acpi_ec *ec)
> >  	return ret;
> >  }
> >
> > -static int acpi_config_boot_ec(struct acpi_ec *ec, bool is_ecdt)
> > +static int acpi_config_boot_ec(struct acpi_ec *ec, acpi_handle handle,
> > +			       bool handle_events, bool is_ecdt)
> >  {
> >  	int ret;
> >
> > -	if (boot_ec)
> > +	/*
> > +	 * Changing the ACPI handle results in a re-configuration of the
> > +	 * boot EC. And if it happens after the namespace initialization,
> > +	 * it causes _REG evaluations.
> > +	 */
> > +	if (boot_ec && boot_ec->handle != handle)
> >  		ec_remove_handlers(boot_ec);
> >
> >  	/* Unset old boot EC */
> >  	if (boot_ec != ec)
> >  		acpi_ec_free(boot_ec);
> >
> > -	ret = acpi_ec_setup(ec);
> > +	/*
> > +	 * ECDT device creation is split into acpi_ec_ecdt_probe() and
> > +	 * acpi_ec_ecdt_start(). This function takes care of completing the
> > +	 * ECDT parsing logic as the handle update should be performed
> > +	 * between the installation/uninstallation of the handlers.
> > +	 */
> > +	if (ec->handle != handle)
> > +		ec->handle = handle;
> > +
> > +	ret = acpi_ec_setup(ec, handle_events);
> >  	if (ret)
> >  		return ret;
> >
> > @@ -1419,9 +1452,12 @@ static int acpi_config_boot_ec(struct acpi_ec *ec, bool is_ecdt)
> >  	if (!boot_ec) {
> >  		boot_ec = ec;
> >  		boot_ec_is_ecdt = is_ecdt;
> > -		acpi_handle_info(boot_ec->handle, "Used as boot %s EC\n",
> > -				 is_ecdt ? "ECDT" : "DSDT");
> >  	}
> > +
> > +	acpi_handle_info(boot_ec->handle,
> > +			 "Used as boot %s EC to handle transactions%s\n",
> > +			 is_ecdt ? "ECDT" : "DSDT",
> > +			 handle_events ? " and events" : "");
> >  	return ret;
> >  }
> >
> > @@ -1442,11 +1478,7 @@ static int acpi_ec_add(struct acpi_device *device)
> >  			goto error;
> >  	}
> >
> > -	/* Find and register all query methods */
> > -	acpi_walk_namespace(ACPI_TYPE_METHOD, ec->handle, 1,
> > -			    acpi_ec_register_query_methods, NULL, ec, NULL);
> > -
> > -	ret = acpi_config_boot_ec(ec, false);
> > +	ret = acpi_config_boot_ec(ec, device->handle, true, false);
> >  	if (ret)
> >  		goto error;
> >
> > @@ -1461,9 +1493,6 @@ static int acpi_ec_add(struct acpi_device *device)
> >  	/* Reprobe devices depending on the EC */
> >  	acpi_walk_dep_device_list(ec->handle);
> >
> > -	/* EC is fully operational, allow queries */
> > -	clear_bit(EC_FLAGS_QUERY_PENDING, &ec->flags);
> > -
> >  	/* Clear stale _Q events if hardware might require that */
> >  	if (EC_FLAGS_CLEAR_ON_RESUME)
> >  		acpi_ec_clear(ec);
> > @@ -1482,7 +1511,6 @@ static int acpi_ec_remove(struct acpi_device *device)
> >
> >  	ec = acpi_driver_data(device);
> >  	ec_remove_handlers(ec);
> > -	acpi_ec_remove_query_handlers(ec, true, 0);
> >  	release_region(ec->data_addr, 1);
> >  	release_region(ec->command_addr, 1);
> >  	device->driver_data = NULL;
> > @@ -1528,9 +1556,8 @@ int __init acpi_ec_dsdt_probe(void)
> >  	if (!ec)
> >  		return -ENOMEM;
> >  	/*
> > -	 * Finding EC from DSDT if there is no ECDT EC available. When this
> > -	 * function is invoked, ACPI tables have been fully loaded, we can
> > -	 * walk namespace now.
> > +	 * At this point, the namespace is initialized, so start to find
> > +	 * the namespace objects.
> >  	 */
> >  	status = acpi_get_devices(ec_device_ids[0].id,
> >  				  ec_parse_device, ec, NULL);
> > @@ -1538,13 +1565,55 @@ int __init acpi_ec_dsdt_probe(void)
> >  		ret = -ENODEV;
> >  		goto error;
> >  	}
> > -	ret = acpi_config_boot_ec(ec, false);
> > +	/*
> > +	 * When the DSDT EC is available, always re-configure boot EC to
> > +	 * have _REG evaluated. _REG can only be evaluated after the
> > +	 * namespace initialization.
> > +	 * At this point, the GPE is not fully initialized, so do not to
> > +	 * handle the events.
> > +	 */
> > +	ret = acpi_config_boot_ec(ec, ec->handle, false, false);
> >  error:
> >  	if (ret)
> >  		acpi_ec_free(ec);
> >  	return ret;
> >  }
> >
> > +/*
> > + * If the DSDT EC is not functioning, we still need to prepare a fully
> > + * functioning ECDT EC first in order to handle the events.
> > + * https://bugzilla.kernel.org/show_bug.cgi?id=115021
> > + */
> > +int __init acpi_ec_ecdt_start(void)
> > +{
> > +	struct acpi_table_ecdt *ecdt_ptr;
> > +	acpi_status status;
> > +	acpi_handle handle;
> > +
> > +	if (!boot_ec)
> > +		return -ENODEV;
> > +	/*
> > +	 * The DSDT EC should have already been started in
> > +	 * acpi_ec_add().
> > +	 */
> > +	if (!boot_ec_is_ecdt)
> > +		return -ENODEV;
> > +
> > +	status = acpi_get_table(ACPI_SIG_ECDT, 1,
> > +				(struct acpi_table_header **)&ecdt_ptr);
> > +	if (ACPI_FAILURE(status))
> > +		return -ENODEV;
> > +
> > +	/*
> > +	 * At this point, the namespace and the GPE is initialized, so
> > +	 * start to find the namespace objects and handle the events.
> > +	 */
> > +	status = acpi_get_handle(NULL, ecdt_ptr->id, &handle);
> > +	if (ACPI_FAILURE(status))
> > +		return -ENODEV;
> > +	return acpi_config_boot_ec(boot_ec, handle, true, true);
> > +}
> > +
> >  #if 0
> >  /*
> >   * Some EC firmware variations refuses to respond QR_EC when SCI_EVT is not
> > @@ -1647,8 +1716,12 @@ int __init acpi_ec_ecdt_probe(void)
> >  		ec->data_addr = ecdt_ptr->data.address;
> >  	}
> >  	ec->gpe = ecdt_ptr->gpe;
> > -	ec->handle = ACPI_ROOT_OBJECT;
> > -	ret = acpi_config_boot_ec(ec, true);
> > +
> > +	/*
> > +	 * At this point, the namespace is not initialized, so do not find
> > +	 * the namespace objects, or handle the events.
> > +	 */
> > +	ret = acpi_config_boot_ec(ec, ACPI_ROOT_OBJECT, false, true);
> >  error:
> >  	if (ret)
> >  		acpi_ec_free(ec);
> > diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h
> > index 940218f..4727722 100644
> > --- a/drivers/acpi/internal.h
> > +++ b/drivers/acpi/internal.h
> > @@ -185,6 +185,7 @@ typedef int (*acpi_ec_query_func) (void *data);
> >  int acpi_ec_init(void);
> >  int acpi_ec_ecdt_probe(void);
> >  int acpi_ec_dsdt_probe(void);
> > +int acpi_ec_ecdt_start(void);
> >  void acpi_ec_block_transactions(void);
> >  void acpi_ec_unblock_transactions(void);
> >  void acpi_ec_unblock_transactions_early(void);
> > diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
> > index ad9fc84..763c0da 100644
> > --- a/drivers/acpi/scan.c
> > +++ b/drivers/acpi/scan.c
> > @@ -2044,6 +2044,7 @@ int __init acpi_scan_init(void)
> >  	}
> >
> >  	acpi_update_all_gpes();
> > +	acpi_ec_ecdt_start();
> 
> Ok, so the order of invocation via acpi_init() is:
> 
> acpi_ec_ecdt_probe() via acpi_bus_init()
> acpi_ec_dsdt_probe() via acpi_bus_init()
> acpi_ec_ecdt_start() via acpi_scan_init
> acpi_ec_add() via acpi_ec_init()
> 
> So the fallback in this patch should work and it should not accidentally
> be activated when the DSDT has a valid EC.

Let me clarify:

acpi_ec_ecdt_probe(): enable EC transactions so that EC operation region can be accessed during the table loading.
acpi_load_tables(): the order of acpi_ec_ecdt_probe()/acpi_load_tables() will be corrected in newer ACPICA releases.
acpi_ec_dsdt_probe(): enable EC transactions so that EC operation region can be accessed in _INI/_STA evaluations.
acpi_initialize_objects(): the order of acpi_ec_dsdt_probe()/acpi_initialize_objects() will be corrected in newer ACPICA releases.
acpi_ec_add(): takes care of multi-EC devices and boot EC/first EC support, driving EC devices, may enable _Qxx handling.
acpi_update_all_gpes()/acpi_ec_ecdt_start(): ensure to enable all _Qxx/_Lxx/_Exx handling at this point.

> 
> Reviewed-by: Peter Wu <peter@xxxxxxxxxxxxx>

Thanks for the review.

Best regards
Lv

> 
> >
> >  	acpi_scan_initialized = true;
> >
> > --
> > 1.7.10
> >

--
To unsubscribe from this list: send the line "unsubscribe linux-acpi" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Linux IBM ACPI]     [Linux Power Management]     [Linux Kernel]     [Linux Laptop]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Video 4 Linux]     [Device Mapper]     [Linux Resources]

  Powered by Linux