My apologies, but I found a few more things that look strange and shouldThank you for reviewing!
be cleaned up. Sorry for this iterative review approach, but I think we're
slowly getting there.
Cheers, DanielWill fix, thank you
---You can't drop the reference while you keep using the object, someone else
+static int xen_drm_drv_dumb_create(struct drm_file *filp,
+ struct drm_device *dev, struct drm_mode_create_dumb *args)
+{
+ struct xen_drm_front_drm_info *drm_info = dev->dev_private;
+ struct drm_gem_object *obj;
+ int ret;
+
+ ret = xen_drm_front_gem_dumb_create(filp, dev, args);
+ if (ret)
+ goto fail;
+
+ obj = drm_gem_object_lookup(filp, args->handle);
+ if (!obj) {
+ ret = -ENOENT;
+ goto fail_destroy;
+ }
+
+ drm_gem_object_unreference_unlocked(obj);
might sneak in and destroy your object. The unreference always must be
last.
You are correct, I need to rework this code+The above also has another race: If you construct an object, then it must
+ /*
+ * In case of CONFIG_DRM_XEN_FRONTEND_CMA gem_obj is constructed
+ * via DRM CMA helpers and doesn't have ->pages allocated
+ * (xendrm_gem_get_pages will return NULL), but instead can provide
+ * sg table
+ */
+ if (xen_drm_front_gem_get_pages(obj))
+ ret = xen_drm_front_dbuf_create_from_pages(
+ drm_info->front_info,
+ xen_drm_front_dbuf_to_cookie(obj),
+ args->width, args->height, args->bpp,
+ args->size,
+ xen_drm_front_gem_get_pages(obj));
+ else
+ ret = xen_drm_front_dbuf_create_from_sgt(
+ drm_info->front_info,
+ xen_drm_front_dbuf_to_cookie(obj),
+ args->width, args->height, args->bpp,
+ args->size,
+ xen_drm_front_gem_get_sg_table(obj));
+ if (ret)
+ goto fail_destroy;
+
be fully constructed by the time you publish it to the wider world. In gem
this is done by calling drm_gem_handle_create() - after that userspace can
get at your object and do nasty things with it in a separate thread,
forcing your driver to Oops if the object isn't fully constructed yet.
That means you need to redo this code here to make sure that the gem
object is fully set up (including pages and sg tables) _before_ anything
calls drm_gem_handle_create().
Although I tend to avoid open-coding, but this seems the necessary measure here
This probably means you also need to open-code the cma side, by first
calling drm_gem_cma_create(), then doing any additional setup, and finally
doing the registration to userspace with drm_gem_handle_create as the very
last thing.
Not sure this will work: nothing prevents you from attaching multiple FBs to a single dumb handle
Alternativet is to do the pages/sg setup only when you create an fb (and
drop the pages again when the fb is destroyed), but that requires some
refcounting/locking in the driver.
Probably I am not sure of which indirection we are talking about, could you please
Aside: There's still a lot of indirection and jumping around which makes
the code a bit hard to follow.
I'll try to play with this:
+This needs to be in the unplug code. Yes that means you'll have multiple
+static void xen_drm_drv_release(struct drm_device *dev)
+{
+ struct xen_drm_front_drm_info *drm_info = dev->dev_private;
+ struct xen_drm_front_info *front_info = drm_info->front_info;
+
+ drm_atomic_helper_shutdown(dev);
+ drm_mode_config_cleanup(dev);
+
+ xen_drm_front_evtchnl_free_all(front_info);
+ dbuf_free_all(&front_info->dbuf_list);
+
+ drm_dev_fini(dev);
+ kfree(dev);
+
+ /*
+ * Free now, as this release could be not due to rmmod, but
+ * due to the backend disconnect, making drm_info hang in
+ * memory until rmmod
+ */
+ devm_kfree(&front_info->xb_dev->dev, front_info->drm_info);
+ front_info->drm_info = NULL;
+
+ /* Tell the backend we are ready to (re)initialize */
+ xenbus_switch_state(front_info->xb_dev, XenbusStateInitialising);
drm_devices floating around, but that's how hotplug works. That would also
mean that you need to drop the front_info pointer from the backend at
unplug time.
If you don't like those semantics then the only other option is to never
destroy the drm_device, but only mark the drm_connector as disconnected
when the xenbus backend is gone. But this half-half solution here where
you hotunplug the drm_device but want to keep it around still doesn't work
from a livetime pov.
Probably misunderstanding comes from the fact that it is possible if backend+static struct xenbus_driver xen_driver = {I still don't understand why you have both the remove and fini versions of
+ .ids = xen_driver_ids,
+ .probe = xen_drv_probe,
+ .remove = xen_drv_remove,
this. See other comments, I think the xenbus vs. drm_device lifetime stuff
still needs to be cleaned up some more. This shouldn't be that hard
really.
Or maybe I'm just totally misunderstanding this frontend vs. backend split
in xen, so if you have a nice gentle intro text for why that exists, it
might help.
That was the idea to allow dummy user-space to get an error in+ /*I'm pretty sure this doesn't work. Especially the check in display_check
+ * pflip_timeout is set to current jiffies once we send a page flip and
+ * reset to 0 when we receive frame done event from the backed.
+ * It is checked during drm_connector_helper_funcs.detect_ctx to detect
+ * time-outs for frame done event, e.g. due to backend errors.
+ *
+ * This must be protected with front_info->io_lock, so races between
+ * interrupt handler and rest of the code are properly handled.
+ */
+ unsigned long pflip_timeout;
+
+ bool conn_connected;
confuses me, if there's ever an error then you'll never ever be able to
display anything again, except when someone disables the display.
If you want to signal errors with the output then this must be doneUnfortunatelly, there is little software available which will benefit
through the new link-status property and
drm_mode_connector_set_link_status_property. Rejecting kms updates in
display_check with -EINVAL because the hw has a temporary issue is kinda
not cool (because many compositors just die when this happens). I thought
we agreed already to remove that, sorry for not spotting that in the
previous version.
I believe you are talking about drm_simple_display_pipe_funcs?
Some of the conn_connected checks also look a bit like they should be
replaced by drm_dev_is_unplugged instead, but I'm not sure.
Ok, will have a dedicated work for that. The reasons why I put this into the+static int connector_detect(struct drm_connector *connector,If you want to check for timeouts please use a worker, don't piggy-pack on
+ struct drm_modeset_acquire_ctx *ctx,
+ bool force)
+{
+ struct xen_drm_front_drm_pipeline *pipeline =
+ to_xen_drm_pipeline(connector);
+ struct xen_drm_front_info *front_info = pipeline->drm_info->front_info;
+ unsigned long flags;
+
+ /* check if there is a frame done event time-out */
+ spin_lock_irqsave(&front_info->io_lock, flags);
+ if (pipeline->pflip_timeout &&
+ time_after_eq(jiffies, pipeline->pflip_timeout)) {
+ DRM_ERROR("Frame done event timed-out\n");
+
+ pipeline->pflip_timeout = 0;
+ pipeline->conn_connected = false;
+ xen_drm_front_kms_send_pending_event(pipeline);
+ }
+ spin_unlock_irqrestore(&front_info->io_lock, flags);
top of the detect callback.
Agree, I will remove this callback completely: I have+static int connector_mode_valid(struct drm_connector *connector,mode_valid on the connector only checks probe modes. Since that is
+ struct drm_display_mode *mode)
+{
+ struct xen_drm_front_drm_pipeline *pipeline =
+ to_xen_drm_pipeline(connector);
+
+ if (mode->hdisplay != pipeline->width)
+ return MODE_ERROR;
+
+ if (mode->vdisplay != pipeline->height)
+ return MODE_ERROR;
+
+ return MODE_OK;
+}
hardcoded this doesn't do much, which means userspace can give you a wrong
mode, and you fall over.
Not sure I understand why do I need to provide a callback here?
You need to use one of the other mode_valid callbacks instead,
drm_simple_display_pipe_funcs has the one you should use.
Not sure how mode_valid is relevant to this code [1]: This function is called+As mentioned, this -EINVAL here needs to go. Since you already have a
+static int display_check(struct drm_simple_display_pipe *pipe,
+ struct drm_plane_state *plane_state,
+ struct drm_crtc_state *crtc_state)
+{
+ struct xen_drm_front_drm_pipeline *pipeline =
+ to_xen_drm_pipeline(pipe);
+
+ return pipeline->conn_connected ? 0 : -EINVAL;
mode_valid callback you can (should) drop this one here entirely.