[Mesa-dev] [PATCH v4 1/2] gallium/winsys/kms: Fix possible leak in map/unmap.
Tomasz Figa
tfiga at chromium.org
Fri Mar 16 07:06:20 UTC 2018
Hi Lepton,
Please avoid top-posting in mailing lists in the future. It's against
the netiquette.
On Wed, Mar 14, 2018 at 10:20 AM, Lepton Wu <lepton at chromium.org> wrote:
[Message moved to bottom]
> On Tue, Mar 13, 2018 at 8:41 AM, Emil Velikov <emil.l.velikov at gmail.com> wrote:
>> On 13 March 2018 at 11:46, Tomasz Figa <tfiga at chromium.org> wrote:
>>> On Thu, Mar 8, 2018 at 7:39 AM, Lepton Wu <lepton at chromium.org> wrote:
>>>> If user calls map twice for kms_sw_displaytarget, the first mapped
>>>> buffer could get leaked. Instead of calling mmap every time, just
>>>> reuse previous mapping. Since user could map same displaytarget with
>>>> different flags, we have to keep two different pointers, one for rw
>>>> mapping and one for ro mapping.
>>>>
>>>> Change-Id: I65308f0ff2640bd57b2577c6a3469540c9722859
>>>> Signed-off-by: Lepton Wu <lepton at chromium.org>
>>>> ---
>>>> .../winsys/sw/kms-dri/kms_dri_sw_winsys.c | 21 ++++++++++++-------
>>>> 1 file changed, 14 insertions(+), 7 deletions(-)
>>>>
>>>> diff --git a/src/gallium/winsys/sw/kms-dri/kms_dri_sw_winsys.c b/src/gallium/winsys/sw/kms-dri/kms_dri_sw_winsys.c
>>>> index 22e1c936ac5..7fc40488c2e 100644
>>>> --- a/src/gallium/winsys/sw/kms-dri/kms_dri_sw_winsys.c
>>>> +++ b/src/gallium/winsys/sw/kms-dri/kms_dri_sw_winsys.c
>>>> @@ -70,6 +70,7 @@ struct kms_sw_displaytarget
>>>>
>>>> uint32_t handle;
>>>> void *mapped;
>>>> + void *ro_mapped;
>>>>
>>>> int ref_count;
>>>> struct list_head link;
>>>> @@ -198,16 +199,19 @@ kms_sw_displaytarget_map(struct sw_winsys *ws,
>>>> return NULL;
>>>>
>>>> prot = (flags == PIPE_TRANSFER_READ) ? PROT_READ : (PROT_READ | PROT_WRITE);
>>>> - kms_sw_dt->mapped = mmap(0, kms_sw_dt->size, prot, MAP_SHARED,
>>>> - kms_sw->fd, map_req.offset);
>>>> -
>>>> - if (kms_sw_dt->mapped == MAP_FAILED)
>>>> - return NULL;
>>>> + void **ptr = (flags == PIPE_TRANSFER_READ) ? &kms_sw_dt->ro_mapped : &kms_sw_dt->mapped;
>>>> + if (!*ptr) {
>>>> + void *tmp = mmap(0, kms_sw_dt->size, prot, MAP_SHARED,
>>>> + kms_sw->fd, map_req.offset);
>>>> + if (tmp == MAP_FAILED)
>>>> + return NULL;
>>>> + *ptr = tmp;
>>>> + }
>>>>
>>>> DEBUG_PRINT("KMS-DEBUG: mapped buffer %u (size %u) at %p\n",
>>>> - kms_sw_dt->handle, kms_sw_dt->size, kms_sw_dt->mapped);
>>>> + kms_sw_dt->handle, kms_sw_dt->size, *ptr);
>>>>
>>>> - return kms_sw_dt->mapped;
>>>> + return *ptr;
>>>> }
>>>>
>>>> static struct kms_sw_displaytarget *
>>>> @@ -278,9 +282,12 @@ kms_sw_displaytarget_unmap(struct sw_winsys *ws,
>>>> struct kms_sw_displaytarget *kms_sw_dt = kms_sw_displaytarget(dt);
>>>>
>>>> DEBUG_PRINT("KMS-DEBUG: unmapped buffer %u (was %p)\n", kms_sw_dt->handle, kms_sw_dt->mapped);
>>>> + DEBUG_PRINT("KMS-DEBUG: unmapped buffer %u (was %p)\n", kms_sw_dt->handle, kms_sw_dt->ro_mapped);
>>>>
>>>> munmap(kms_sw_dt->mapped, kms_sw_dt->size);
>>>> kms_sw_dt->mapped = NULL;
>>>> + munmap(kms_sw_dt->ro_mapped, kms_sw_dt->size);
>>>> + kms_sw_dt->ro_mapped = NULL;
>>>> }
>>>
>>> If user calls map twice, wouldn't they also call unmap twice?
>>> Moreover, wouldn't the pointer be expected to be still valid between
>>> first and second unmap?
>>>
>>> The answer obviously depends on how the API is designed, but i feels
>>> really weird being asymmetrical like that. Typically the mapping would
>>> be refcounted and maps would have to be balanced with unmaps to free
>>> the mapping.
>>>
>> Valid points.
>>
>> If you guys prefer we could land 2/2 (multiplane support), since it
>> has no dependency of the mapping work.
>> And polish out ro/rw mappings (even the leaks) at later stage, as time permits?
>>
>> -Emil
>
> I am fine to add ref count for map pointer but then the code looks a
> little complex:
> We already have ref count for display target, and it seems most other
> drivers don't have a
> ref count for map pointer. (I checked dri_sw_displaytarget_map /
> gdi_sw_displaytarget_map/hgl_winsys_displaytarget_map
> /xlib_displaytarget_map)
>
> If you really want a reference count for map pointer, I will add one.
> But I am just feeling that introduce some unnecessary complex.
> Just want to get confirmation before I begin to write code.
Looking at users, I found at least one that it's quite realistic to
have two separate maps created and expected to work, because of
semantics of pipe->transfer_map() API:
https://cgit.freedesktop.org/mesa/mesa/tree/src/gallium/drivers/softpipe/sp_texture.c#n355
You can see that every time it creates a new, independent
pipe_transfer object, which should be expected to be valid until
pipe->transfer_unmap() on this particular object is not called.
Also, looking at the implementations you mentioned:
1) dri_sw
The real buffer is not being mapped. There is a staging buffer
allocated with malloc() at display target creation time and data are
read-back to it in dri_sw_displaytarget_map() and written-back to the
real buffer in dri_sw_displaytarget_unmap(). The dri_sw_dt->mapped is
not needed for anything.
2) gdi_sw, hgl_winsys
The buffer is allocated by malloc_aligned() and so map() simply
returns that pointer and unmap() is a no-op.
3) xlib
The buffer is either an shmem or malloc buffer and the behavior is
essentially the same as 2). The xlib_dt->mapped variable is not needed
for anything.
So, if we are mapping the real buffer, we need to reference count the mapping.
Best regards,
Tomasz
More information about the mesa-dev
mailing list