Requests are read from CT in the irq handler, but actual processing
will be done in the work thread. Processing of specific actions will
be added in the upcoming patches.

v2: don't use GEM_BUG_ON (Chris)
    don't kmalloc too large buffer (Michal)

Signed-off-by: Michal Wajdeczko <michal.wajdec...@intel.com>
Cc: Oscar Mateo <oscar.ma...@intel.com>
Cc: Michel Thierry <michel.thie...@intel.com>
Cc: Daniele Ceraolo Spurio <daniele.ceraolospu...@intel.com>
---
 drivers/gpu/drm/i915/intel_guc_ct.c | 71 ++++++++++++++++++++++++++++++++++++-
 drivers/gpu/drm/i915/intel_guc_ct.h |  4 +++
 2 files changed, 74 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/i915/intel_guc_ct.c 
b/drivers/gpu/drm/i915/intel_guc_ct.c
index 3187bf2..abf2bd8 100644
--- a/drivers/gpu/drm/i915/intel_guc_ct.c
+++ b/drivers/gpu/drm/i915/intel_guc_ct.c
@@ -32,10 +32,17 @@ struct ct_request {
        u32 *response_buf;
 };
 
+struct ct_incoming_request {
+       struct list_head link;
+       u32 data[];
+};
+
 enum { CTB_SEND = 0, CTB_RECV = 1 };
 
 enum { CTB_OWNER_HOST = 0 };
 
+static void ct_worker_func(struct work_struct *w);
+
 void intel_guc_ct_init_early(struct intel_guc_ct *ct)
 {
        /* we're using static channel owners */
@@ -43,6 +50,8 @@ void intel_guc_ct_init_early(struct intel_guc_ct *ct)
 
        spin_lock_init(&ct->lock);
        INIT_LIST_HEAD(&ct->pending_requests);
+       INIT_LIST_HEAD(&ct->incoming_requests);
+       INIT_WORK(&ct->worker, ct_worker_func);
 }
 
 static inline const char *guc_ct_buffer_type_to_str(u32 type)
@@ -608,14 +617,74 @@ static int guc_handle_response(struct intel_guc *guc, 
const u32 *msg)
 static int guc_handle_request(struct intel_guc *guc, const u32 *msg)
 {
        u32 header = msg[0];
+       u32 len = ct_header_get_len(header) + 1; /* total len with header */
+       struct ct_incoming_request *request;
+       unsigned long flags;
 
        GEM_BUG_ON(ct_header_is_response(header));
        /* Request message layout beyond header is request specific */
 
-       /* XXX */
+       request = kmalloc(sizeof(*request) + 4*len, GFP_ATOMIC);
+       if (unlikely(!request)) {
+               DRM_ERROR("CT: dropping request %*phn\n", 4*len, msg);
+               return 0; /* XXX: -ENOMEM ? */
+       }
+       memcpy(request->data, msg, 4*len);
+
+       spin_lock_irqsave(&guc->ct.lock, flags);
+       list_add_tail(&request->link, &guc->ct.incoming_requests);
+       spin_unlock_irqrestore(&guc->ct.lock, flags);
+
+       queue_work(system_unbound_wq, &guc->ct.worker);
        return 0;
 }
 
+static bool guc_process_incoming_requests(struct intel_guc *guc)
+{
+       unsigned long flags;
+       struct ct_incoming_request *request;
+       bool done;
+       u32 header;
+       u32 action;
+       u32 len;
+
+       spin_lock_irqsave(&guc->ct.lock, flags);
+       request = list_first_entry_or_null(&guc->ct.incoming_requests,
+                                          struct ct_incoming_request, link);
+       if (request)
+               list_del(&request->link);
+       done = !!list_empty(&guc->ct.incoming_requests);
+       spin_unlock_irqrestore(&guc->ct.lock, flags);
+
+       if (!request)
+               return true;
+
+       header = request->data[0];
+       action = ct_header_get_action(header);
+       len = ct_header_get_len(header) + 1; /* also count header dw */
+
+       switch (action) {
+       default:
+               DRM_ERROR("CT: unexpected request %*phn\n",
+                         4*len, request->data);
+               break;
+       }
+
+       kfree(request);
+       return done;
+}
+
+static void ct_worker_func(struct work_struct *w)
+{
+       struct intel_guc_ct *ct = container_of(w, struct intel_guc_ct, worker);
+       struct intel_guc *guc = container_of(ct, struct intel_guc, ct);
+       bool done;
+
+       done = guc_process_incoming_requests(guc);
+       if (!done)
+               queue_work(system_unbound_wq, &ct->worker);
+}
+
 static void intel_guc_receive_ct(struct intel_guc *guc)
 {
        struct intel_guc_ct_channel *ctch = &guc->ct.host_channel;
diff --git a/drivers/gpu/drm/i915/intel_guc_ct.h 
b/drivers/gpu/drm/i915/intel_guc_ct.h
index 557c1e8..125e004 100644
--- a/drivers/gpu/drm/i915/intel_guc_ct.h
+++ b/drivers/gpu/drm/i915/intel_guc_ct.h
@@ -73,6 +73,8 @@ struct intel_guc_ct_channel {
  * @host_channel: main channel used by the host
  * @lock: spin lock for pending requests list
  * @pending_requests: list of pending requests
+ * @incoming_requests: list of incoming requests
+ * @tasklet: tasklet for handling incoming requests
  */
 struct intel_guc_ct {
        struct intel_guc_ct_channel host_channel;
@@ -80,6 +82,8 @@ struct intel_guc_ct {
 
        spinlock_t lock;
        struct list_head pending_requests;
+       struct list_head incoming_requests;
+       struct work_struct worker;
 };
 
 void intel_guc_ct_init_early(struct intel_guc_ct *ct);
-- 
2.7.4

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

Reply via email to