clear-pkgs-linux-iot-lts2018/0942-media-intel-ipu4-VIRT-...

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 39f3ad39aefc..c8451b28e8d5 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 f5b6e3964ec2..cc9870cb8082 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 7774b5f64c22..939fc62496c3 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 ab5626cf6734..a0d672123b9a 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 b0adf273bf0b..a4e0d87e224f 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 5e3b53c9c6e1..c0ac79fef8e2 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
+}
--
https://clearlinux.org