mei: don't get stuck in select during reset
authorTomas Winkler <tomas.winkler@intel.com>
Thu, 25 Jul 2013 17:15:53 +0000 (20:15 +0300)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 27 Jul 2013 00:54:30 +0000 (17:54 -0700)
Clear pending connection after hw reset but before hw start
and wake up the waiting task in poll. Signal POLLERR in select
when device went through reset.

Add wrapper mei_cl_is_connected for checking if
the device and client are connected.

Signed-off-by: Tomas Winkler <tomas.winkler@intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/misc/mei/amthif.c
drivers/misc/mei/client.c
drivers/misc/mei/client.h
drivers/misc/mei/init.c
drivers/misc/mei/main.c

index 749452f8e2f67c90140de2f30a88b9cd522724b5..d0fdc134068a05ed9967fe98ca7d9a6d6429e710 100644 (file)
@@ -418,15 +418,23 @@ unsigned int mei_amthif_poll(struct mei_device *dev,
                struct file *file, poll_table *wait)
 {
        unsigned int mask = 0;
-       mutex_unlock(&dev->device_lock);
+
        poll_wait(file, &dev->iamthif_cl.wait, wait);
+
        mutex_lock(&dev->device_lock);
-       if (dev->iamthif_state == MEI_IAMTHIF_READ_COMPLETE &&
-               dev->iamthif_file_object == file) {
+       if (!mei_cl_is_connected(&dev->iamthif_cl)) {
+
+               mask = POLLERR;
+
+       } else if (dev->iamthif_state == MEI_IAMTHIF_READ_COMPLETE &&
+                  dev->iamthif_file_object == file) {
+
                mask |= (POLLIN | POLLRDNORM);
                dev_dbg(&dev->pdev->dev, "run next amthif cb\n");
                mei_amthif_run_next_cmd(dev);
        }
+       mutex_unlock(&dev->device_lock);
+
        return mask;
 }
 
index af1e60205140ae14a976037212d2f481fe304abf..e0684b4d9a08aebd26453de23c5b88fb69b6b7d7 100644 (file)
@@ -635,10 +635,7 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length)
 
        dev = cl->dev;
 
-       if (cl->state != MEI_FILE_CONNECTED)
-               return -ENODEV;
-
-       if (dev->dev_state != MEI_DEV_ENABLED)
+       if (!mei_cl_is_connected(cl))
                return -ENODEV;
 
        if (cl->read_cb) {
index 9bae4c724603b3d3ac70346e4deefcf6bceedb19..9eb031e920701e8ad8feadf3bec2900b182e4a5c 100644 (file)
@@ -84,6 +84,13 @@ int mei_cl_flow_ctrl_reduce(struct mei_cl *cl);
 /*
  *  MEI input output function prototype
  */
+static inline bool mei_cl_is_connected(struct mei_cl *cl)
+{
+       return (cl->dev &&
+               cl->dev->dev_state == MEI_DEV_ENABLED &&
+               cl->state == MEI_FILE_CONNECTED);
+}
+
 bool mei_cl_is_other_connecting(struct mei_cl *cl);
 int mei_cl_disconnect(struct mei_cl *cl);
 int mei_cl_connect(struct mei_cl *cl, struct file *file);
index 7929e14627bafa6defd1c535415b36e9cd6d71d2..557eed2a159520fb31dbde10c36a74d9c7a07bc7 100644 (file)
@@ -149,12 +149,20 @@ void mei_reset(struct mei_device *dev, int interrupts_enabled)
        dev->hbm_state = MEI_HBM_IDLE;
 
        if (dev->dev_state != MEI_DEV_INITIALIZING) {
+
                if (dev->dev_state != MEI_DEV_DISABLED &&
                    dev->dev_state != MEI_DEV_POWER_DOWN)
                        dev->dev_state = MEI_DEV_RESETTING;
 
+
+               /* remove all waiting requests */
+               mei_cl_all_write_clear(dev);
+
                mei_cl_all_disconnect(dev);
 
+               /* wake up all readings so they can be interrupted */
+               mei_cl_all_wakeup(dev);
+
                /* remove entry if already in list */
                dev_dbg(&dev->pdev->dev, "remove iamthif and wd from the file list.\n");
                mei_cl_unlink(&dev->wd_cl);
@@ -195,11 +203,6 @@ void mei_reset(struct mei_device *dev, int interrupts_enabled)
 
        mei_hbm_start_req(dev);
 
-       /* wake up all readings so they can be interrupted */
-       mei_cl_all_wakeup(dev);
-
-       /* remove all waiting requests */
-       mei_cl_all_write_clear(dev);
 }
 EXPORT_SYMBOL_GPL(mei_reset);
 
index 5e11b5b9b65d2b9096164691f7feaf3a54c00ffc..173ff095be0dd6747145c9653726b3e00f85cd81 100644 (file)
@@ -625,24 +625,32 @@ static unsigned int mei_poll(struct file *file, poll_table *wait)
        unsigned int mask = 0;
 
        if (WARN_ON(!cl || !cl->dev))
-               return mask;
+               return POLLERR;
 
        dev = cl->dev;
 
        mutex_lock(&dev->device_lock);
 
-       if (dev->dev_state != MEI_DEV_ENABLED)
-               goto out;
-
-
-       if (cl == &dev->iamthif_cl) {
-               mask = mei_amthif_poll(dev, file, wait);
+       if (!mei_cl_is_connected(cl)) {
+               mask = POLLERR;
                goto out;
        }
 
        mutex_unlock(&dev->device_lock);
+
+
+       if (cl == &dev->iamthif_cl)
+               return mei_amthif_poll(dev, file, wait);
+
        poll_wait(file, &cl->tx_wait, wait);
+
        mutex_lock(&dev->device_lock);
+
+       if (!mei_cl_is_connected(cl)) {
+               mask = POLLERR;
+               goto out;
+       }
+
        if (MEI_WRITE_COMPLETE == cl->writing_state)
                mask |= (POLLIN | POLLRDNORM);