Re: [PATCH v3 1/5] ACPI: change __init to __ref for early_acpi_os_unmap_memory()
From: Aleksey Makarov
Date: Fri Feb 26 2016 - 05:34:13 EST
On 02/26/2016 09:39 AM, Zheng, Lv wrote:
> Hi, Aleksey
>
> Though I promised you to have this done in 1 week:
> https://github.com/zetalog/acpica/commit/a234569
> I implemented the idea and tried the cleanup in Linux.
> But unfortunately the cleanup triggered several locking issues inside of ACPICA table API users.
> So it doesn't seem to be so easy and quick to make it upstreamed.
> And I have to continue to test and improve the cleanup.
>
> I'm sorry for that, hope you can find substitute solutions before the cleanup is upstreamed.
That's fine, I use the approach I described in the last mail, it works.
Thank you
Aleksey Makarov
>
> Thanks and best regards
> -Lv
>
>> From: Aleksey Makarov [mailto:aleksey.makarov@xxxxxxxxxx]
>> Sent: Monday, February 22, 2016 10:58 PM
>> Subject: Re: [PATCH v3 1/5] ACPI: change __init to __ref for
>> early_acpi_os_unmap_memory()
>>
>> Hi,
>>
>> On 02/22/2016 05:24 AM, Zheng, Lv wrote:
>>> Hi,
>>>
>>>> From: Aleksey Makarov [mailto:aleksey.makarov@xxxxxxxxxx]
>>>> Subject: Re: [PATCH v3 1/5] ACPI: change __init to __ref for
>>>> early_acpi_os_unmap_memory()
>>>>
>>>> Hi Lv,
>>>>
>>>> On 02/19/2016 05:58 AM, Zheng, Lv wrote:
>>>>> Hi,
>>>>>
>>>>>> From: Peter Hurley [mailto:peter@xxxxxxxxxxxxxxxxxx]
>>>>>> Subject: Re: [PATCH v3 1/5] ACPI: change __init to __ref for
>>>>>> early_acpi_os_unmap_memory()
>>>>>>
>>>>>> On 02/17/2016 07:36 PM, Zheng, Lv wrote:
>>>>>>> Hi,
>>>>>>>
>>>>>>>> From: Aleksey Makarov [mailto:aleksey.makarov@xxxxxxxxxx]
>>>>>>>> Subject: Re: [PATCH v3 1/5] ACPI: change __init to __ref for
>>>>>>>> early_acpi_os_unmap_memory()
>>>>>>>>
>>>>>>>> Hi Lv,
>>>>>>>>
>>>>>>>> Thank you for review.
>>>>>>>>
>>>>>>>> On 02/17/2016 05:51 AM, Zheng, Lv wrote:
>>>>>>>>
>>>>>>>> [..]
>>>>>>>>
>>>>>>>>>>> early_acpi_os_unmap_memory() is marked as __init because it calls
>>>>>>>>>>> __acpi_unmap_table(), but only when acpi_gbl_permanent_mmap
>> is
>>>> not
>>>>>>>> set.
>>>>>>>>>>>
>>>>>>>>>>> acpi_gbl_permanent_mmap is set in __init acpi_early_init()
>>>>>>>>>>> so it is safe to call early_acpi_os_unmap_memory() from anywhere
>>>>>>>>>>>
>>>>>>>>>>> We need this function to be non-__init because we need access to
>>>>>>>>>>> some tables at unpredictable time--it may be before or after
>>>>>>>>>>> acpi_gbl_permanent_mmap is set. For example, SPCR (Serial Port
>>>>>> Console
>>>>>>>>>>> Redirection) table is needed each time a new console is registered.
>>>>>>>>>>> It can be quite early (console_initcall) or when a module is inserted.
>>>>>>>>>>> When this table accessed before acpi_gbl_permanent_mmap is set,
>>>>>>>>>>> the pointer should be unmapped. This is exactly what this function
>>>>>>>>>>> does.
>>>>>>>>>> [Lv Zheng]
>>>>>>>>>> Why don't you use another API instead of
>>>>>> early_acpi_os_unmap_memory()
>>>>>>>> in
>>>>>>>>>> case you want to unmap things in any cases.
>>>>>>>>>> acpi_os_unmap_memory() should be the one to match this purpose.
>>>>>>>>>> It checks acpi_gbl_ppermanent_mmap in acpi_os_unmap_iomem().
>>>>>>>>
>>>>>>>> As far as I understand, there exist two steps in ACPI initialization:
>>>>>>>>
>>>>>>>> 1. Before acpi_gbl_permanent_mmap is set, tables received with
>>>>>>>> acpi_get_table_with_size()
>>>>>>>> are mapped by early_memremap(). If a subsystem gets such a pointer
>> it
>>>>>>>> should be unmapped.
>>>>>>>>
>>>>>>>> 2 After acpi_gbl_permanent_mmap is set this pointer should not be
>>>>>> unmapped
>>>>>>>> at all.
>>>>>>>>
>>>>>>> [Lv Zheng]
>>>>>>> This statement is wrong, this should be:
>>>>>>> As long as there is a __reference__ to the mapped table, the pointer
>> should
>>>>>> not be unmapped.
>>>>>>> In fact, we have a series to introduce acpi_put_table() to achieve this.
>>>>>>> So your argument is wrong from very first point.
>>>>>>>
>>>>>>>> That exactly what early_acpi_os_unmap_memory() does--it checks
>>>>>>>> acpi_gbl_permanent_mmap.
>>>>>>>> If I had used acpi_os_unmap_memory() after
>> acpi_gbl_permanent_mmap
>>>>>> had
>>>>>>>> been set,
>>>>>>>> it would have tried to free that pointer with an oops (actually, I
>> checked
>>>> that
>>>>>>>> and got that oops).
>>>>>>>>
>>>>>>>> So using acpi_os_unmap_memory() is not an option here, but
>>>>>>>> early_acpi_os_unmap_memory()
>>>>>>>> match perfectly.
>>>>>>> [Lv Zheng]
>>>>>>> I don't think so.
>>>>>>> For definition block tables, we know for sure there will always be
>>>> references,
>>>>>> until "Unload" opcode is invoked by the AML interpreter.
>>>>>>> But for the data tables, OSPMs should use them in this way:
>>>>>>> 1. map the table
>>>>>>> 2. parse the table and convert it to OS specific structures
>>>>>>> 3. unmap the table
>>>>>>> This helps to shrink virtual memory address space usages.
>>>>>>>
>>>>>>> So from this point of view, all data tables should be unmapped right
>> after
>>>>>> being parsed.
>>>>>>> Why do you need the map to be persistent in the kernel address space?
>>>>>>> You can always map a small table, but what if the table size is very big?
>>>>>>>
>>>>>>>>
>>>>>>>>>> And in fact early_acpi_os_unmap_memory() should be removed.
>>>>>>>>
>>>>>>>> I don't think so -- I have explained why. It does different thing.
>>>>>>>> Probably it (and/or other functions in that api) should be renamed.
>>>>>>>>
>>>>>>> [Lv Zheng]
>>>>>>> Just let me ask one more question.
>>>>>>> eary_acpi_os_unmap_memory() is not used inside of ACPICA.
>>>>>>> How ACPICA can work with just acpi_os_unmap_memory()?
>>>>>>> You can check drivers/acpi/tbxxx.c.
>>>>>>> Especially: acpi_tb_release_temp_table() and the code invoking it.
>>>>>>>
>>>>>>>>> [Lv Zheng]
>>>>>>>>> One more thing is:
>>>>>>>>> If you can't switch your driver to use acpi_os_unmap_memory()
>> instead
>>>> of
>>>>>>>> early_acpi_os_unmap_memory(),
>>>>>>>>> then it implies that your driver does have a defect.
>>>>>>>>
>>>>>>>> I still don't understand what defect, sorry.
>>>>>>> [Lv Zheng]
>>>>>>> If you can't ensure this sequence for using the data tables:
>>>>>>> 1. map the table
>>>>>>> 2. parse the table and convert it to OS specific structure
>>>>>>> 3. unmap the table
>>>>>>> It implies there is a bug in the driver or a bug in the ACPI subsystem core.
>>>>>>
>>>>>> Exactly.
>>>>> [Lv Zheng]
>>>>> So it looks to me:
>>>>> Changing __init to __ref here is entirely not acceptable.
>>>>> This API should stay being invoked during early stage.
>>>>> Otherwise, it may leave us untrackable code that maps tables during early
>>>> stage and leaks maps to the late stage.
>>>>> If Linux contains such kind of code, I'm afraid, it will become impossible to
>>>> introduce acpi_put_table() to clean up the mappings.
>>>>> Because when acpi_put_table() is called during the late stage to free a map
>>>> acquired during the early stage, it then obviously will end up with panic.
>>>>
>>>> Can you please sugggest a common method to access ACPI tables that
>>>> works both before *and* after acpi_gbl_permanent_mmap is set and __init
>>>> code
>>>> is removed?
>>> [Lv Zheng]
>>> Do not change __init for now.
>>>
>>> Currently you should:
>>> 1. before acpi_gbl_permanent_mmap is set:
>>> acpi_get_table_with_size()
>>> parse the table
>>> early_acpi_os_unmap_memory()
>>> Do your driver early stuff here
>>>
>>> 2. after acpi_gbl_permanent_mmap is set:
>>> acpi_get_table()
>>> Parse the table
>>> Do your driver late stuff here
>>> <- note there is no API now being an inverse of acpi_get_table().
>>
>> That's fine. These are two different methods to access the table.
>> I need one that works in both cases. Of course, they could be combined,
>> but I am not sure if I can access the acpi_gbl_permanent_mmap variable--it
>> seems to be an implementation detail of ACPI code.
>>
>> Instead I am going to use the 1st method once and cache the result like this:
>>
>>
>> static int __init parse(void)
>> {
>> static bool parsed;
>>
>> if (!parsed) {
>> acpi_get_table_with_size()
>> /* parse the table and cache the result */
>> early_acpi_os_unmap_memory()
>> parse = true;
>> }
>> }
>>
>> arch_initcal(parse());
>>
>> int __ref the_handler_where_I_need_the_parse_results(void)
>> {
>> parse();
>>
>> /* use the data */
>> }
>>
>> I hope you are OK with it.
>>
>>> Besides, I'm about to insert error messages between 1 and 2.
>>> If an early map is not release, error message will be prompted to the
>> developers.
>>
>> AFAICS, there is such an error message and I saw it.
>> Refer to check_early_ioremap_leak() at mm/early_ioremap.c
>>
>>> And if you don't follow the above rules, it mean you are trying to lay a mine,
>> waiting for me to step on it.
>>> That's why this change is entirely not acceptable.
>>
>> Ok, I see.
>>
>>> I'm about to send out the cleanup series in 1 week, and will Cc you.
>>> You can rebase your code on top of the cleanup series.
>>
>> Thank you
>> Aleksey Makarov
>>
>>>
>>> Thanks and best regards
>>> -Lv
>>>
>>>>
>>>>> Thanks and best regards
>>>>> -Lv
>>>>>
>>>>>> The central problem here is the way Aleksey is trying to hookup a console.
>>>>>>
>>>>>> What should be happening in this series is:
>>>>>> 1. early map SPCR
>>>>>> 2. parse the SPCR table
>>>>>> 3. call add_preferred_console() to add the SPCR console to the console
>>>> table
>>>>>> 4. unmap SPCR
>>>>>>
>>>>>> This trivial and unobtrusive method would already have a 8250 console
>>>>>> running via SPCR. I've already pointed this out in previous reviews.
>>>>>>
>>>>>> Further, the above method *will be required anyway* for the DBG2 table
>> to
>>>>>> start an earlycon, which I've already pointed out in previous reviews.
>>>>>>
>>>>>> Then to enable amba-pl011 console via ACPI, add a console match()
>> method
>>>>>> similar to the 8250 console match() method, univ8250_console_match().
>>>>>>
>>>>>> FWIW, PCI earlycon + console support was already submitted once before
>>>> but
>>>>>> never picked up by GregKH. I think I'll just grab that and re-submit so
>>>>>> you would know what to emit for console options in the
>>>>>> add_preferred_console().
>>>>>>
>>>>>>
>>>>>> Regards,
>>>>>> Peter Hurley
>>>>>>
>>>>>>
>>>>>>>>> There is an early bootup requirement in Linux.
>>>>>>>>> Maps acquired during the early stage should be freed by the driver
>> during
>>>>>> the
>>>>>>>> early stage.
>>>>>>>>> And the driver should re-acquire the memory map after booting.
>>>>>>>>
>>>>>>>> Exactly. That's why I use early_acpi_os_unmap_memory(). The point is
>>>> that
>>>>>>>> that code can be
>>>>>>>> called *before* acpi_gbl_permanent_mmap is set (at early initialization
>>>> of
>>>>>> for
>>>>>>>> example 8250 console)
>>>>>>>> or *after* acpi_gbl_permanent_mmap is set (at insertion of a module
>>>> that
>>>>>>>> registers a console),
>>>>>>>> We just can not tell if the received table pointer should or sould not be
>>>> freed
>>>>>>>> with early_memunmap()
>>>>>>>> (actually __acpi_unmap_table() or whatever) without checking
>>>>>>>> acpi_gbl_permanent_mmap,
>>>>>>>> but that's all too low level.
>>>>>>> [Lv Zheng]
>>>>>>> The driver should make sure that:
>>>>>>> Map/unmap is paired during early stage.
>>>>>>> For late stage, it should be another pair of map/unmap.
>>>>>>>
>>>>>>>>
>>>>>>>> Another option, as you describe, is to get this pointer early, don't free it
>>>>>>> [Lv Zheng]
>>>>>>> I mean you should free it early.
>>>>>>>
>>>>>>>> untill acpi_gbl_permanent_mmap is set, then free it (with
>>>>>>>> early_acpi_os_unmap_memory(), that's
>>>>>>>> ok because acpi_gbl_permanent_mmap is set in an init code), then get
>>>> the
>>>>>>>> persistent
>>>>>>>> pointer to the table. The problem with it is that we can not just watch
>>>>>>>> acpi_gbl_permanent_mmap
>>>>>>>> to catch this exact moment. And also accessing
>>>> acpi_gbl_permanent_mmap
>>>>>> is
>>>>>>>> not good as it probably is
>>>>>>>> an implementation detail (i. e. too lowlevel) of the ACPI code.
>>>>>>>> Or I probably miss what you are suggesting.
>>>>>>>>
>>>>>>> [Lv Zheng]
>>>>>>> I mean, you should:
>>>>>>> During early stage:
>>>>>>> acpi_os_map_memory()
>>>>>>> Parse the table.
>>>>>>> acpi_os_unmap_memory().
>>>>>>>
>>>>>>>>> This is because, during early bootup stage, there are only limited slots
>>>>>>>> available for drivers to map memory.
>>>>>>>>> So by changing __init to __ref here, you probably will hide many such
>>>>>> defects.
>>>>>>>>
>>>>>>>> What defects? This funcions checks acpi_gbl_permanent_mmap.
>>>>>>>> BTW, exactly the same thing is done in the beginning of
>>>>>>>> acpi_os_unmap_memory() and than's ok,
>>>>>>>> that function is __ref.
>>>>>>>>
>>>>>>>>> And solving issues in this way doesn't seem to be an improvement.
>>>>>>>>
>>>>>>>> Could you please tell me where I am wrong? I still don't understand
>> your
>>>>>> point.
>>>>>>> [Lv Zheng]
>>>>>>> But anyway, the defect should be in ACPI subsystem core.
>>>>>>> The cause should be the API itself - acpi_get_table().
>>>>>>>
>>>>>>> So I agree you can use early_acpi_os_unmap_memory() during the
>> period
>>>> the
>>>>>> root causes are not cleaned up.
>>>>>>> But the bottom line is: the driver need to ensure that
>>>>>> early_acpi_os_unmap_memory() is always invoked.
>>>>>>> As long as you can ensure this, I don't have objections for deploying
>>>>>> early_acpi_os_unmap_memory() for now.
>>>>>>>
>>>>>>> Thanks and best regards
>>>>>>> -Lv
>>>>>>>
>>>>>>>>
>>>>>>>> Thank you
>>>>>>>> Aleksey Makarov
>>>>>>>>
>>>>>>>>>
>>>>>>>>> Thanks and best regards
>>>>>>>>> -Lv
>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> Thanks and best regards
>>>>>>>>>> -Lv
>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> Signed-off-by: Aleksey Makarov <aleksey.makarov@xxxxxxxxxx>
>>>>>>>>>>> ---
>>>>>>>>>>> drivers/acpi/osl.c | 6 +++++-
>>>>>>>>>>> 1 file changed, 5 insertions(+), 1 deletion(-)
>>>>>>>>>>>
>>>>>>>>>>> diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c
>>>>>>>>>>> index 67da6fb..8a552cd 100644
>>>>>>>>>>> --- a/drivers/acpi/osl.c
>>>>>>>>>>> +++ b/drivers/acpi/osl.c
>>>>>>>>>>> @@ -497,7 +497,11 @@ void __ref acpi_os_unmap_memory(void
>>>> *virt,
>>>>>>>>>>> acpi_size size)
>>>>>>>>>>> }
>>>>>>>>>>> EXPORT_SYMBOL_GPL(acpi_os_unmap_memory);
>>>>>>>>>>>
>>>>>>>>>>> -void __init early_acpi_os_unmap_memory(void __iomem *virt,
>>>>>> acpi_size
>>>>>>>>>> size)
>>>>>>>>>>> +/*
>>>>>>>>>>> + * acpi_gbl_permanent_mmap is set in __init acpi_early_init()
>>>>>>>>>>> + * so it is safe to call early_acpi_os_unmap_memory() from
>>>> anywhere
>>>>>>>>>>> + */
>>>>>>>>>>> +void __ref early_acpi_os_unmap_memory(void __iomem *virt,
>>>>>> acpi_size
>>>>>>>>>> size)
>>>>>>>>>>> {
>>>>>>>>>>> if (!acpi_gbl_permanent_mmap)
>>>>>>>>>>> __acpi_unmap_table(virt, size);
>>>>>>>>>>> --
>>>>>>>>>>> 2.7.1
>>>>>>>>>>>
>>>>>>>>>>> --
>>>>>>>>>>> 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
>>>>>>>>>> --
>>>>>>>>>> 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
>>>>>