Skip to content

Commit 4ee6e67

Browse files
author
Oleksandr Andrushchenko
committed
drm/xen-zcopy: Implement dumb free wait IOCTL
1. Provide wait_handle while calling DRM_XEN_ZCOPY_DUMB_FROM_REFS IOCTL. 2. Implement IOCTL which will block until dumb buffer with the wait handle provided be freed: this is needed for synchronization between frontend and backend in case frontend provides grant references of the buffer via DRM_XEN_ZCOPY_DUMB_FROM_REFS IOCTL and which must be released before backend replies with XENDISPL_OP_DBUF_DESTROY response. Signed-off-by: Oleksandr Andrushchenko <[email protected]> Reviewed-by: Volodymyr Babchuk <[email protected]>
1 parent dad52fd commit 4ee6e67

File tree

2 files changed

+266
-12
lines changed

2 files changed

+266
-12
lines changed

drivers/gpu/drm/xen/xen_drm_zcopy_drv.c

Lines changed: 245 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,42 @@ struct xen_gem_object {
5454
struct sg_table *sgt;
5555
/* map grant handles */
5656
grant_handle_t *map_handles;
57+
/*
58+
* this is used for synchronous object deletion, e.g.
59+
* when user-space wants to know that the grefs are unmapped
60+
*/
61+
struct kref refcount;
62+
int wait_handle;
63+
};
64+
65+
struct xen_wait_obj {
66+
struct list_head list;
67+
struct xen_gem_object *xen_obj;
68+
struct completion completion;
69+
};
70+
71+
struct xen_drv_info {
72+
struct drm_device *drm_dev;
73+
74+
/*
75+
* for buffers created from front's grant references synchronization
76+
* between backend and frontend is needed on buffer deletion as front
77+
* expects us to unmap these references after XENDISPL_OP_DBUF_DESTROY
78+
* response
79+
* the rationale behind implementing own wait handle:
80+
* - dumb buffer handle cannot be used as when the PRIME buffer
81+
* gets exported there are at least 2 handles: one is for the
82+
* backend and another one for the importing application,
83+
* so when backend closes its handle and the other application still
84+
* holds the buffer then there is no way for the backend to tell
85+
* which buffer we want to wait for while calling xen_ioctl_wait_free
86+
* - flink cannot be used as well as it is gone when DRM core
87+
* calls .gem_free_object_unlocked
88+
*/
89+
struct list_head wait_obj_list;
90+
struct idr idr;
91+
spinlock_t idr_lock;
92+
spinlock_t wait_list_lock;
5793
};
5894

5995
static inline struct xen_gem_object *to_xen_gem_obj(
@@ -62,6 +98,113 @@ static inline struct xen_gem_object *to_xen_gem_obj(
6298
return container_of(gem_obj, struct xen_gem_object, base);
6399
}
64100

101+
static struct xen_wait_obj *xen_wait_obj_new(struct xen_drv_info *drv_info,
102+
struct xen_gem_object *xen_obj)
103+
{
104+
struct xen_wait_obj *wait_obj;
105+
106+
wait_obj = kzalloc(sizeof(*wait_obj), GFP_KERNEL);
107+
if (!wait_obj)
108+
return ERR_PTR(-ENOMEM);
109+
110+
init_completion(&wait_obj->completion);
111+
wait_obj->xen_obj = xen_obj;
112+
spin_lock(&drv_info->wait_list_lock);
113+
list_add(&wait_obj->list, &drv_info->wait_obj_list);
114+
spin_unlock(&drv_info->wait_list_lock);
115+
return wait_obj;
116+
}
117+
118+
static void xen_wait_obj_free(struct xen_drv_info *drv_info,
119+
struct xen_wait_obj *wait_obj)
120+
{
121+
struct xen_wait_obj *cur_wait_obj, *q;
122+
123+
spin_lock(&drv_info->wait_list_lock);
124+
list_for_each_entry_safe(cur_wait_obj, q,
125+
&drv_info->wait_obj_list, list) {
126+
if (cur_wait_obj == wait_obj) {
127+
list_del(&wait_obj->list);
128+
kfree(wait_obj);
129+
break;
130+
}
131+
}
132+
spin_unlock(&drv_info->wait_list_lock);
133+
}
134+
135+
static void xen_wait_obj_check_pending(struct xen_drv_info *drv_info)
136+
{
137+
/*
138+
* it is intended to be called from .last_close when
139+
* no pending wait objects should be on the list.
140+
* make sure we don't miss a bug if this is not the case
141+
*/
142+
if (!list_empty(&drv_info->wait_obj_list)) {
143+
DRM_ERROR("Removing with pending wait objects!\n");
144+
BUG();
145+
}
146+
}
147+
148+
static int xen_wait_obj_wait(struct xen_wait_obj *wait_obj,
149+
uint32_t wait_to_ms)
150+
{
151+
if (wait_for_completion_timeout(&wait_obj->completion,
152+
msecs_to_jiffies(wait_to_ms)) <= 0)
153+
return -ETIMEDOUT;
154+
155+
return 0;
156+
}
157+
158+
static void xen_wait_obj_signal(struct xen_drv_info *drv_info,
159+
struct xen_gem_object *xen_obj)
160+
{
161+
struct xen_wait_obj *wait_obj, *q;
162+
163+
spin_lock(&drv_info->wait_list_lock);
164+
list_for_each_entry_safe(wait_obj, q, &drv_info->wait_obj_list, list) {
165+
if (wait_obj->xen_obj == xen_obj) {
166+
DRM_DEBUG("Found xen_obj in the wait list, wake\n");
167+
complete_all(&wait_obj->completion);
168+
}
169+
}
170+
spin_unlock(&drv_info->wait_list_lock);
171+
}
172+
173+
static int xen_wait_obj_handle_new(struct xen_drv_info *drv_info,
174+
struct xen_gem_object *xen_obj)
175+
{
176+
int ret;
177+
178+
idr_preload(GFP_KERNEL);
179+
spin_lock(&drv_info->idr_lock);
180+
ret = idr_alloc(&drv_info->idr, xen_obj, 1, 0, GFP_NOWAIT);
181+
spin_unlock(&drv_info->idr_lock);
182+
idr_preload_end();
183+
return ret;
184+
}
185+
186+
static void xen_wait_obj_handle_free(struct xen_drv_info *drv_info,
187+
struct xen_gem_object *xen_obj)
188+
{
189+
spin_lock(&drv_info->idr_lock);
190+
idr_remove(&drv_info->idr, xen_obj->wait_handle);
191+
spin_unlock(&drv_info->idr_lock);
192+
}
193+
194+
static struct xen_gem_object *xen_get_obj_by_wait_handle(
195+
struct xen_drv_info *drv_info, int wait_handle)
196+
{
197+
struct xen_gem_object *xen_obj;
198+
199+
spin_lock(&drv_info->idr_lock);
200+
/* check if xen_obj still exists */
201+
xen_obj = idr_find(&drv_info->idr, wait_handle);
202+
if (xen_obj)
203+
kref_get(&xen_obj->refcount);
204+
spin_unlock(&drv_info->idr_lock);
205+
return xen_obj;
206+
}
207+
65208
#ifdef CONFIG_DRM_XEN_ZCOPY_CMA
66209
static int xen_alloc_ballooned_pages(struct device *dev,
67210
struct xen_gem_object *xen_obj)
@@ -413,10 +556,22 @@ static int xen_gem_init_obj(struct xen_gem_object *xen_obj,
413556
return 0;
414557
}
415558

559+
static void xen_obj_release(struct kref *kref)
560+
{
561+
struct xen_gem_object *xen_obj =
562+
container_of(kref, struct xen_gem_object, refcount);
563+
struct xen_drv_info *drv_info = xen_obj->base.dev->dev_private;
564+
565+
xen_wait_obj_signal(drv_info, xen_obj);
566+
kfree(xen_obj);
567+
}
568+
416569
static void xen_gem_free_object(struct drm_gem_object *gem_obj)
417570
{
418571
struct xen_gem_object *xen_obj = to_xen_gem_obj(gem_obj);
572+
struct xen_drv_info *drv_info = gem_obj->dev->dev_private;
419573

574+
DRM_DEBUG("Freeing dumb with handle %d\n", xen_obj->dumb_handle);
420575
if (xen_obj->grefs) {
421576
if (xen_obj->sgt) {
422577
if (xen_obj->base.import_attach)
@@ -428,7 +583,9 @@ static void xen_gem_free_object(struct drm_gem_object *gem_obj)
428583
}
429584
}
430585
drm_gem_object_release(gem_obj);
431-
kfree(xen_obj);
586+
587+
xen_wait_obj_handle_free(drv_info, xen_obj);
588+
kref_put(&xen_obj->refcount, xen_obj_release);
432589
}
433590

434591
#ifdef CONFIG_DRM_XEN_ZCOPY_WA_SWIOTLB
@@ -495,6 +652,7 @@ struct drm_gem_object *xen_gem_prime_import_sg_table(struct drm_device *dev,
495652
ret = xen_gem_init_obj(xen_obj, dev, attach->dmabuf->size);
496653
if (ret < 0)
497654
goto fail;
655+
kref_init(&xen_obj->refcount);
498656
xen_obj->sgt = sgt;
499657
xen_obj->num_pages = DIV_ROUND_UP(attach->dmabuf->size, PAGE_SIZE);
500658
DRM_DEBUG("Imported buffer of size %zu with nents %u\n",
@@ -510,12 +668,14 @@ static int xen_do_ioctl_from_refs(struct drm_device *dev,
510668
struct drm_xen_zcopy_dumb_from_refs *req,
511669
struct drm_file *file_priv)
512670
{
671+
struct xen_drv_info *drv_info = dev->dev_private;
513672
struct xen_gem_object *xen_obj;
514673
int ret;
515674

516675
xen_obj = kzalloc(sizeof(*xen_obj), GFP_KERNEL);
517676
if (!xen_obj)
518677
return -ENOMEM;
678+
kref_init(&xen_obj->refcount);
519679
xen_obj->num_pages = req->num_grefs;
520680
xen_obj->otherend_id = req->otherend_id;
521681
xen_obj->grefs = kcalloc(xen_obj->num_pages, sizeof(grant_ref_t),
@@ -538,6 +698,19 @@ static int xen_do_ioctl_from_refs(struct drm_device *dev,
538698
goto fail;
539699
/* return handle */
540700
req->dumb.handle = xen_obj->dumb_handle;
701+
702+
/*
703+
* get user-visible handle for this GEM object.
704+
* the wait object is not allocated at the moment,
705+
* but if need be it will be allocated at the time of
706+
* DRM_XEN_ZCOPY_DUMB_WAIT_FREE IOCTL
707+
*/
708+
ret = xen_wait_obj_handle_new(drv_info, xen_obj);
709+
if (ret < 0)
710+
goto fail;
711+
712+
req->wait_handle = ret;
713+
xen_obj->wait_handle = ret;
541714
return 0;
542715

543716
fail:
@@ -643,13 +816,61 @@ static int xen_ioctl_to_refs(struct drm_device *dev,
643816
return ret;
644817
}
645818

819+
static int xen_ioctl_wait_free(struct drm_device *dev,
820+
void *data, struct drm_file *file_priv)
821+
{
822+
struct drm_xen_zcopy_dumb_wait_free *req =
823+
(struct drm_xen_zcopy_dumb_wait_free *)data;
824+
struct xen_drv_info *drv_info = dev->dev_private;
825+
struct xen_gem_object *xen_obj;
826+
struct xen_wait_obj *wait_obj;
827+
int wait_handle, ret;
828+
829+
wait_handle = req->wait_handle;
830+
/*
831+
* try to find the wait handle: if not found means that
832+
* either the handle has already been freed or wrong
833+
*/
834+
xen_obj = xen_get_obj_by_wait_handle(drv_info, wait_handle);
835+
if (!xen_obj)
836+
return -ENOENT;
837+
838+
/*
839+
* xen_obj still exists and is reference count locked by us now, so
840+
* prepare to wait: allocate wait object and add it to the wait list,
841+
* so we can find it on release
842+
*/
843+
wait_obj = xen_wait_obj_new(drv_info, xen_obj);
844+
/* put our reference and wait for xen_obj release to fire */
845+
kref_put(&xen_obj->refcount, xen_obj_release);
846+
ret = PTR_ERR_OR_ZERO(wait_obj);
847+
if (ret < 0) {
848+
DRM_ERROR("Failed to setup wait object, ret %d\n", ret);
849+
return ret;
850+
}
851+
852+
ret = xen_wait_obj_wait(wait_obj, req->wait_to_ms);
853+
xen_wait_obj_free(drv_info, wait_obj);
854+
return ret;
855+
}
856+
857+
static void xen_lastclose(struct drm_device *dev)
858+
{
859+
struct xen_drv_info *drv_info = dev->dev_private;
860+
861+
xen_wait_obj_check_pending(drv_info);
862+
}
863+
646864
static const struct drm_ioctl_desc xen_ioctls[] = {
647865
DRM_IOCTL_DEF_DRV(XEN_ZCOPY_DUMB_FROM_REFS,
648866
xen_ioctl_from_refs,
649867
DRM_AUTH | DRM_CONTROL_ALLOW | DRM_UNLOCKED),
650868
DRM_IOCTL_DEF_DRV(XEN_ZCOPY_DUMB_TO_REFS,
651869
xen_ioctl_to_refs,
652870
DRM_AUTH | DRM_CONTROL_ALLOW | DRM_UNLOCKED),
871+
DRM_IOCTL_DEF_DRV(XEN_ZCOPY_DUMB_WAIT_FREE,
872+
xen_ioctl_wait_free,
873+
DRM_AUTH | DRM_CONTROL_ALLOW | DRM_UNLOCKED),
653874
};
654875

655876
static const struct file_operations xen_fops = {
@@ -661,6 +882,7 @@ static const struct file_operations xen_fops = {
661882

662883
static struct drm_driver xen_driver = {
663884
.driver_features = DRIVER_GEM | DRIVER_PRIME,
885+
.lastclose = xen_lastclose,
664886
.prime_handle_to_fd = drm_gem_prime_handle_to_fd,
665887
.gem_prime_export = drm_gem_prime_export,
666888
.gem_prime_get_sg_table = xen_gem_prime_get_sg_table,
@@ -680,42 +902,53 @@ static struct drm_driver xen_driver = {
680902

681903
static int xen_remove(struct platform_device *pdev)
682904
{
683-
struct drm_device *drm_dev = platform_get_drvdata(pdev);
905+
struct xen_drv_info *drv_info = platform_get_drvdata(pdev);
684906

685-
if (drm_dev) {
686-
drm_dev_unregister(drm_dev);
687-
drm_dev_unref(drm_dev);
907+
if (drv_info && drv_info->drm_dev) {
908+
drm_dev_unregister(drv_info->drm_dev);
909+
drm_dev_unref(drv_info->drm_dev);
910+
idr_destroy(&drv_info->idr);
688911
}
689912
return 0;
690913
}
691914

692915
static int xen_probe(struct platform_device *pdev)
693916
{
694-
struct drm_device *drm_dev;
917+
struct xen_drv_info *drv_info;
695918
int ret;
696919

697920
DRM_INFO("Creating %s\n", xen_driver.desc);
921+
drv_info = kzalloc(sizeof(*drv_info), GFP_KERNEL);
922+
if (!drv_info)
923+
return -ENOMEM;
924+
925+
idr_init(&drv_info->idr);
926+
spin_lock_init(&drv_info->idr_lock);
927+
spin_lock_init(&drv_info->wait_list_lock);
928+
INIT_LIST_HEAD(&drv_info->wait_obj_list);
698929
#ifdef CONFIG_DRM_XEN_ZCOPY_CMA
699930
arch_setup_dma_ops(&pdev->dev, 0, 0, NULL, false);
700931
#endif
701-
drm_dev = drm_dev_alloc(&xen_driver, &pdev->dev);
702-
if (!drm_dev)
932+
drv_info->drm_dev = drm_dev_alloc(&xen_driver, &pdev->dev);
933+
if (!drv_info->drm_dev)
703934
return -ENOMEM;
704935

705-
ret = drm_dev_register(drm_dev, 0);
936+
ret = drm_dev_register(drv_info->drm_dev, 0);
706937
if (ret < 0)
707938
goto fail;
708939

709-
platform_set_drvdata(pdev, drm_dev);
940+
drv_info->drm_dev->dev_private = drv_info;
941+
platform_set_drvdata(pdev, drv_info);
710942

711943
DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n",
712944
xen_driver.name, xen_driver.major,
713945
xen_driver.minor, xen_driver.patchlevel,
714-
xen_driver.date, drm_dev->primary->index);
946+
xen_driver.date, drv_info->drm_dev->primary->index);
715947
return 0;
716948

717949
fail:
718-
drm_dev_unref(drm_dev);
950+
drm_dev_unref(drv_info->drm_dev);
951+
kfree(drv_info);
719952
return ret;
720953
}
721954

0 commit comments

Comments
 (0)