388 lines
13 KiB
Diff
388 lines
13 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Ong Hock Yu <ong.hock.yu@intel.com>
|
|
Date: Wed, 16 Jan 2019 01:21:27 +0000
|
|
Subject: [PATCH] media: intel-ipu4: [VIRT] Clean up SOS states after UOS
|
|
reboot.
|
|
|
|
Under condition when the stream did not get
|
|
properly close when UOS rebooted, this leave
|
|
SOS states out of sync with UOS after come
|
|
back. Buffers that has been released by UOS
|
|
still being held in SOS list and caused panic.
|
|
|
|
Also added spinlock and use safe version of
|
|
hash and list iterator to protect against
|
|
entry delection.
|
|
|
|
Change-Id: Ib525371960ea8606d36685830c694b32a82ef096
|
|
Tracked-On: PKT-1691
|
|
Tracked-On: OAM-75231
|
|
Signed-off-by: Ong Hock Yu <ong.hock.yu@intel.com>
|
|
Signed-off-by: spoluri <sarat.chandra.poluri@intel.com>
|
|
---
|
|
.../media/pci/intel/ici/ici-isys-frame-buf.c | 44 ++++++--------
|
|
drivers/media/pci/intel/ici/ici-isys-stream.c | 10 +++-
|
|
.../intel/virtio/intel-ipu4-para-virt-psys.c | 4 +-
|
|
.../virtio/intel-ipu4-virtio-be-stream.c | 59 ++++++++++++++++---
|
|
.../pci/intel/virtio/intel-ipu4-virtio-be.c | 10 +++-
|
|
.../intel/virtio/intel-ipu4-virtio-common.c | 6 +-
|
|
6 files changed, 93 insertions(+), 40 deletions(-)
|
|
|
|
diff --git a/drivers/media/pci/intel/ici/ici-isys-frame-buf.c b/drivers/media/pci/intel/ici/ici-isys-frame-buf.c
|
|
index 39f3ad3..c8451b2 100644
|
|
--- a/drivers/media/pci/intel/ici/ici-isys-frame-buf.c
|
|
+++ b/drivers/media/pci/intel/ici/ici-isys-frame-buf.c
|
|
@@ -583,6 +583,7 @@ int ici_isys_put_buf(struct ici_isys_stream *as,
|
|
unsigned int f_flags)
|
|
{
|
|
struct ici_frame_buf_wrapper *buf;
|
|
+ struct ici_frame_buf_wrapper *buf_safe;
|
|
struct ici_isys_frame_buf_list *buf_list = &as->buf_list;
|
|
unsigned long flags = 0;
|
|
int rval;
|
|
@@ -608,7 +609,7 @@ int ici_isys_put_buf(struct ici_isys_stream *as,
|
|
|
|
// FIXME: This is different from ICG V4L2 implementation which uses time stamp
|
|
// to sort frames
|
|
- list_for_each_entry(buf, &buf_list->putbuf_list, node) {
|
|
+ list_for_each_entry_safe(buf, buf_safe, &buf_list->putbuf_list, node) {
|
|
if (buf->state == ICI_BUF_READY && buf->frame_info.frame_buf_id ==
|
|
frame_info->frame_buf_id) {
|
|
list_del(&buf->node);
|
|
@@ -747,16 +748,12 @@ void ici_isys_frame_buf_stream_cancel(struct
|
|
{
|
|
struct ici_isys_frame_buf_list *buf_list = &as->buf_list;
|
|
struct ici_frame_buf_wrapper *buf;
|
|
+ struct ici_frame_buf_wrapper *bufsafe;
|
|
unsigned long flags = 0;
|
|
|
|
- while (1) {
|
|
- spin_lock_irqsave(&buf_list->lock, flags);
|
|
- if (list_empty(&buf_list->getbuf_list)) {
|
|
- spin_unlock_irqrestore(&buf_list->lock, flags);
|
|
- break;
|
|
- }
|
|
- buf = list_entry(buf_list->getbuf_list.next,
|
|
- struct ici_frame_buf_wrapper, node);
|
|
+ spin_lock_irqsave(&buf_list->lock, flags);
|
|
+ list_for_each_entry_safe(buf, bufsafe,
|
|
+ &buf_list->getbuf_list, node) {
|
|
list_del(&buf->node);
|
|
spin_unlock_irqrestore(&buf_list->lock, flags);
|
|
dev_dbg(&buf_list->strm_dev->dev, "buf: %p\n", buf);
|
|
@@ -764,16 +761,13 @@ void ici_isys_frame_buf_stream_cancel(struct
|
|
unmap_buf(buf);
|
|
else
|
|
unmap_buf_virt(buf);
|
|
+ spin_lock_irqsave(&buf_list->lock, flags);
|
|
}
|
|
+ spin_unlock_irqrestore(&buf_list->lock, flags);
|
|
|
|
- while (1) {
|
|
- spin_lock_irqsave(&buf_list->lock, flags);
|
|
- if (list_empty(&buf_list->putbuf_list)) {
|
|
- spin_unlock_irqrestore(&buf_list->lock, flags);
|
|
- break;
|
|
- }
|
|
- buf = list_entry(buf_list->putbuf_list.next,
|
|
- struct ici_frame_buf_wrapper, node);
|
|
+ spin_lock_irqsave(&buf_list->lock, flags);
|
|
+ list_for_each_entry_safe(buf, bufsafe,
|
|
+ &buf_list->putbuf_list, node) {
|
|
list_del(&buf->node);
|
|
spin_unlock_irqrestore(&buf_list->lock, flags);
|
|
dev_dbg(&buf_list->strm_dev->dev, "buf: %p\n", buf);
|
|
@@ -781,22 +775,20 @@ void ici_isys_frame_buf_stream_cancel(struct
|
|
unmap_buf(buf);
|
|
else
|
|
unmap_buf_virt(buf);
|
|
+ spin_lock_irqsave(&buf_list->lock, flags);
|
|
}
|
|
+ spin_unlock_irqrestore(&buf_list->lock, flags);
|
|
|
|
- while (1) {
|
|
- spin_lock_irqsave(&buf_list->short_packet_queue_lock, flags);
|
|
- if (list_empty(&buf_list->interlacebuf_list)) {
|
|
- spin_unlock_irqrestore
|
|
- (&buf_list->short_packet_queue_lock, flags);
|
|
- break;
|
|
- }
|
|
- buf = list_entry(buf_list->interlacebuf_list.next,
|
|
- struct ici_frame_buf_wrapper, node);
|
|
+ spin_lock_irqsave(&buf_list->short_packet_queue_lock, flags);
|
|
+ list_for_each_entry_safe(buf, bufsafe,
|
|
+ &buf_list->interlacebuf_list, node) {
|
|
list_del(&buf->node);
|
|
spin_unlock_irqrestore(&buf_list->short_packet_queue_lock, flags);
|
|
dev_dbg(&buf_list->strm_dev->dev, "buf: %p\n", buf);
|
|
unmap_buf(buf);
|
|
+ spin_lock_irqsave(&buf_list->short_packet_queue_lock, flags);
|
|
}
|
|
+ spin_unlock_irqrestore(&buf_list->short_packet_queue_lock, flags);
|
|
}
|
|
|
|
int ici_isys_frame_buf_add_next(
|
|
diff --git a/drivers/media/pci/intel/ici/ici-isys-stream.c b/drivers/media/pci/intel/ici/ici-isys-stream.c
|
|
index f5b6e39..cc9870c 100644
|
|
--- a/drivers/media/pci/intel/ici/ici-isys-stream.c
|
|
+++ b/drivers/media/pci/intel/ici/ici-isys-stream.c
|
|
@@ -957,11 +957,18 @@ static int ici_isys_stream_off(struct file *file, void *fh)
|
|
if (ip->streaming)
|
|
ici_isys_set_streaming(as, 0);
|
|
|
|
- ip->streaming = 0;
|
|
ici_isys_frame_buf_short_packet_destroy(as);
|
|
mutex_unlock(&as->isys->stream_mutex);
|
|
|
|
ici_isys_frame_buf_stream_cancel(as);
|
|
+
|
|
+ mutex_lock(&as->isys->stream_mutex);
|
|
+ //streaming always should be turned off last.
|
|
+ //This variable prevents other streams from
|
|
+ //starting before we are done with cleanup.
|
|
+ ip->streaming = 0;
|
|
+ mutex_unlock(&as->isys->stream_mutex);
|
|
+
|
|
pipeline_set_power(as, 0);
|
|
return 0;
|
|
}
|
|
@@ -1453,7 +1460,6 @@ void ici_isys_stream_cleanup(struct ici_isys_stream *as)
|
|
stream_device_unregister(&as->strm_dev);
|
|
node_pads_cleanup(&as->asd->node);
|
|
mutex_destroy(&as->mutex);
|
|
- //intel_ipu4_isys_framebuf_cleanup(&as->buf_list);
|
|
}
|
|
|
|
#endif //ICI_ENABLED
|
|
diff --git a/drivers/media/pci/intel/virtio/intel-ipu4-para-virt-psys.c b/drivers/media/pci/intel/virtio/intel-ipu4-para-virt-psys.c
|
|
index 7774b5f..939fc62 100644
|
|
--- a/drivers/media/pci/intel/virtio/intel-ipu4-para-virt-psys.c
|
|
+++ b/drivers/media/pci/intel/virtio/intel-ipu4-para-virt-psys.c
|
|
@@ -719,6 +719,7 @@ static int virt_psys_release(struct inode *inode, struct file *file)
|
|
struct ipu4_virtio_req *req;
|
|
struct ipu4_virtio_ctx *fe_ctx = psys->ctx;
|
|
struct ipu_psys_buffer_wrap *psys_buf_wrap;
|
|
+ struct hlist_node *tmp;
|
|
struct virt_ipu_psys_fh *fh = file->private_data;
|
|
int rval = 0, bkt;
|
|
|
|
@@ -746,7 +747,8 @@ static int virt_psys_release(struct inode *inode, struct file *file)
|
|
mutex_lock(&fh->mutex);
|
|
/* clean up buffers */
|
|
if(!hash_empty(FD_BUF_HASH)) {
|
|
- hash_for_each(FD_BUF_HASH, bkt, psys_buf_wrap, node) {
|
|
+ hash_for_each_safe(FD_BUF_HASH, bkt, tmp,
|
|
+ psys_buf_wrap, node) {
|
|
psys_put_userpages(&psys_buf_wrap->map);
|
|
hash_del(&psys_buf_wrap->node);
|
|
kfree(psys_buf_wrap);
|
|
diff --git a/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be-stream.c b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be-stream.c
|
|
index ab5626c..a0d6721 100644
|
|
--- a/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be-stream.c
|
|
+++ b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be-stream.c
|
|
@@ -27,6 +27,7 @@
|
|
|
|
DECLARE_HASHTABLE(STREAM_NODE_HASH, MAX_SIZE);
|
|
static bool hash_initialised;
|
|
+static spinlock_t stream_node_hash_lock;
|
|
|
|
struct stream_node {
|
|
int client_id;
|
|
@@ -34,8 +35,34 @@ struct stream_node {
|
|
struct hlist_node node;
|
|
};
|
|
|
|
-int process_device_open(struct ipu4_virtio_req_info *req_info)
|
|
+void cleanup_stream(void)
|
|
{
|
|
+ struct stream_node *sn = NULL;
|
|
+ unsigned long flags = 0;
|
|
+ int bkt;
|
|
+ struct hlist_node *tmp;
|
|
+
|
|
+ //To clean up SOS when uos got rebooted and stream did not
|
|
+ //get closed properly. Current implementation only handle
|
|
+ //for single UOS.
|
|
+ spin_lock_irqsave(&stream_node_hash_lock, flags);
|
|
+ if (!hash_empty(STREAM_NODE_HASH)) {
|
|
+ hash_for_each_safe(STREAM_NODE_HASH, bkt, tmp, sn, node) {
|
|
+ if (sn != NULL) {
|
|
+ pr_debug("%s: performing stream clean up!",
|
|
+ __func__);
|
|
+ filp_close(sn->f, 0);
|
|
+ hash_del(&sn->node);
|
|
+ kfree(sn);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ spin_unlock_irqrestore(&stream_node_hash_lock, flags);
|
|
+}
|
|
+
|
|
+static int process_device_open(struct ipu4_virtio_req_info *req_info)
|
|
+{
|
|
+ unsigned long flags = 0;
|
|
char node_name[25];
|
|
struct stream_node *sn = NULL;
|
|
struct ici_stream_device *strm_dev;
|
|
@@ -45,17 +72,27 @@ int process_device_open(struct ipu4_virtio_req_info *req_info)
|
|
if (!hash_initialised) {
|
|
hash_init(STREAM_NODE_HASH);
|
|
hash_initialised = true;
|
|
+ spin_lock_init(&stream_node_hash_lock);
|
|
}
|
|
+
|
|
+ spin_lock_irqsave(&stream_node_hash_lock, flags);
|
|
hash_for_each_possible(STREAM_NODE_HASH, sn, node, req->op[0]) {
|
|
if (sn != NULL) {
|
|
if (sn->client_id != domid) {
|
|
- pr_err("process_device_open: stream device %d already opened by other guest!", sn->client_id);
|
|
+ pr_err("%s: stream device %d already opened by other guest!",
|
|
+ __func__, sn->client_id);
|
|
+ spin_unlock_irqrestore(&stream_node_hash_lock,
|
|
+ flags);
|
|
return IPU4_REQ_ERROR;
|
|
}
|
|
- pr_info("process_device_open: stream device %d already opened by client %d", req->op[0], domid);
|
|
- return IPU4_REQ_PROCESSED;
|
|
+ pr_info("%s: stream device %d already opened by client %d",
|
|
+ __func__, req->op[0], domid);
|
|
+ spin_unlock_irqrestore(&stream_node_hash_lock,
|
|
+ flags);
|
|
+ return IPU4_REQ_ERROR;
|
|
}
|
|
}
|
|
+ spin_unlock_irqrestore(&stream_node_hash_lock, flags);
|
|
|
|
sprintf(node_name, "/dev/intel_stream%d", req->op[0]);
|
|
pr_info("process_device_open: %s", node_name);
|
|
@@ -70,14 +107,18 @@ int process_device_open(struct ipu4_virtio_req_info *req_info)
|
|
strm_dev->virt_dev_id = req->op[0];
|
|
|
|
sn->client_id = domid;
|
|
+ spin_lock_irqsave(&stream_node_hash_lock, flags);
|
|
hash_add(STREAM_NODE_HASH, &sn->node, req->op[0]);
|
|
+ spin_unlock_irqrestore(&stream_node_hash_lock, flags);
|
|
|
|
return IPU4_REQ_PROCESSED;
|
|
}
|
|
|
|
-int process_device_close(struct ipu4_virtio_req_info *req_info)
|
|
+static int process_device_close(struct ipu4_virtio_req_info *req_info)
|
|
{
|
|
+ unsigned long flags = 0;
|
|
struct stream_node *sn = NULL;
|
|
+ struct hlist_node *tmp;
|
|
struct ipu4_virtio_req *req = req_info->request;
|
|
|
|
if (!hash_initialised)
|
|
@@ -85,13 +126,16 @@ int process_device_close(struct ipu4_virtio_req_info *req_info)
|
|
|
|
pr_info("process_device_close: %d", req->op[0]);
|
|
|
|
- hash_for_each_possible(STREAM_NODE_HASH, sn, node, req->op[0]) {
|
|
+ spin_lock_irqsave(&stream_node_hash_lock, flags);
|
|
+ hash_for_each_possible_safe(STREAM_NODE_HASH, sn,
|
|
+ tmp, node, req->op[0]) {
|
|
if (sn != NULL) {
|
|
- hash_del(&sn->node);
|
|
filp_close(sn->f, 0);
|
|
+ hash_del(&sn->node);
|
|
kfree(sn);
|
|
}
|
|
}
|
|
+ spin_unlock_irqrestore(&stream_node_hash_lock, flags);
|
|
|
|
return IPU4_REQ_PROCESSED;
|
|
}
|
|
@@ -176,6 +220,7 @@ int process_poll(struct ipu4_virtio_req_info *req_info)
|
|
}
|
|
|
|
as = dev_to_stream(sn->f->private_data);
|
|
+
|
|
spin_lock_irqsave(&as->buf_list.lock, flags);
|
|
empty = list_empty(&as->buf_list.putbuf_list);
|
|
spin_unlock_irqrestore(&as->buf_list.lock, flags);
|
|
diff --git a/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be.c b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be.c
|
|
index b0adf27..a4e0d87 100644
|
|
--- a/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be.c
|
|
+++ b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be.c
|
|
@@ -65,6 +65,8 @@ static void ipu_vbk_stop_vq(struct ipu4_virtio_be_priv *rng,
|
|
static void ipu_vbk_flush_vq(struct ipu4_virtio_be_priv *rng, int index);
|
|
#endif
|
|
|
|
+extern void cleanup_stream(void);
|
|
+
|
|
/* hash table related functions */
|
|
static void ipu_vbk_hash_init(void)
|
|
{
|
|
@@ -107,6 +109,7 @@ static struct ipu4_virtio_be_priv *ipu_vbk_hash_find(int client_id)
|
|
static int ipu_vbk_hash_del(int client_id)
|
|
{
|
|
struct ipu4_virtio_be_priv *entry;
|
|
+ struct hlist_node *tmp;
|
|
int bkt;
|
|
|
|
if (!ipu_vbk_hash_initialized) {
|
|
@@ -114,7 +117,7 @@ static int ipu_vbk_hash_del(int client_id)
|
|
return -1;
|
|
}
|
|
|
|
- hash_for_each(HASH_NAME, bkt, entry, node)
|
|
+ hash_for_each_safe(HASH_NAME, bkt, tmp, entry, node)
|
|
if (virtio_dev_client_id(&entry->dev) == client_id) {
|
|
hash_del(&entry->node);
|
|
return 0;
|
|
@@ -128,6 +131,7 @@ static int ipu_vbk_hash_del(int client_id)
|
|
static int ipu_vbk_hash_del_all(void)
|
|
{
|
|
struct ipu4_virtio_be_priv *entry;
|
|
+ struct hlist_node *tmp;
|
|
int bkt;
|
|
|
|
if (!ipu_vbk_hash_initialized) {
|
|
@@ -135,7 +139,7 @@ static int ipu_vbk_hash_del_all(void)
|
|
return -1;
|
|
}
|
|
|
|
- hash_for_each(HASH_NAME, bkt, entry, node)
|
|
+ hash_for_each_safe(HASH_NAME, bkt, tmp, entry, node)
|
|
hash_del(&entry->node);
|
|
|
|
return 0;
|
|
@@ -306,6 +310,8 @@ static int ipu_vbk_release(struct inode *inode, struct file *f)
|
|
pr_err("%s: UNLIKELY rng NULL!\n",
|
|
__func__);
|
|
|
|
+ cleanup_stream();
|
|
+
|
|
ipu_vbk_stop(priv);
|
|
ipu_vbk_flush(priv);
|
|
for (i = 0; i < IPU_VIRTIO_QUEUE_MAX; i++)
|
|
diff --git a/drivers/media/pci/intel/virtio/intel-ipu4-virtio-common.c b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-common.c
|
|
index 5e3b53c..c0ac79f 100644
|
|
--- a/drivers/media/pci/intel/virtio/intel-ipu4-virtio-common.c
|
|
+++ b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-common.c
|
|
@@ -63,9 +63,11 @@ struct ipu4_virtio_fe_info *ipu4_virtio_fe_find_by_vmid(int vmid)
|
|
int ipu4_virtio_fe_remove(int client_id)
|
|
{
|
|
struct ipu4_virtio_fe_info_entry *info_entry;
|
|
+ struct hlist_node *tmp;
|
|
int bkt;
|
|
|
|
- hash_for_each(ipu4_virtio_fe_hash, bkt, info_entry, node)
|
|
+ hash_for_each_safe(ipu4_virtio_fe_hash, bkt,
|
|
+ tmp, info_entry, node)
|
|
if (info_entry->info->client_id == client_id) {
|
|
hash_del(&info_entry->node);
|
|
kfree(info_entry);
|
|
@@ -135,4 +137,4 @@ void *ipu4_virtio_ring_pop(struct ipu4_virtio_ring *ring)
|
|
ring->used--;
|
|
|
|
return data;
|
|
-}
|
|
\ No newline at end of file
|
|
+}
|
|
--
|
|
2.21.0
|
|
|