Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

mei: wd: decouple and revamp watchdog state machine

Before ME watchdog was exported through standard watchdog interface
it was closed and started together with the mei device.

The major issue is that closing ME watchdog disabled also MEI device,
to fix this the watchdog state machine has to be independent from MEI
state machine.

Signed-off-by: Tomas Winkler <tomas.winkler@intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Tomas Winkler and committed by
Greg Kroah-Hartman
c216fdeb 248ffdf7

+34 -27
-1
drivers/misc/mei/init.c
··· 330 330 331 331 dev->me_clients_num = 0; 332 332 dev->rd_msg_hdr = 0; 333 - dev->stop = false; 334 333 dev->wd_pending = false; 335 334 336 335 /* update the state of the registers after reset */
+1 -1
drivers/misc/mei/interface.h
··· 56 56 57 57 58 58 int mei_wd_send(struct mei_device *dev); 59 - int mei_wd_stop(struct mei_device *dev, bool preserve); 59 + int mei_wd_stop(struct mei_device *dev); 60 60 int mei_wd_host_init(struct mei_device *dev); 61 61 /* 62 62 * mei_watchdog_register - Registering watchdog interface
+3 -6
drivers/misc/mei/interrupt.c
··· 1224 1224 } 1225 1225 } 1226 1226 1227 - if (dev->stop && !dev->wd_pending) { 1228 - dev->wd_stopped = true; 1227 + if (dev->wd_state == MEI_WD_STOPPING) { 1228 + dev->wd_state = MEI_WD_IDLE; 1229 1229 wake_up_interruptible(&dev->wait_stop_wd); 1230 - return 0; 1231 1230 } 1232 1231 1233 1232 if (dev->extra_write_index) { ··· 1249 1250 1250 1251 dev->wd_pending = false; 1251 1252 1252 - if (dev->wd_timeout) 1253 + if (dev->wd_state == MEI_WD_RUNNING) 1253 1254 *slots -= mei_data2slots(MEI_WD_START_MSG_SIZE); 1254 1255 else 1255 1256 *slots -= mei_data2slots(MEI_WD_STOP_MSG_SIZE); 1256 1257 } 1257 1258 } 1258 - if (dev->stop) 1259 - return -ENODEV; 1260 1259 1261 1260 /* complete control write list CB */ 1262 1261 dev_dbg(&dev->pdev->dev, "complete control write list cb.\n");
+7 -2
drivers/misc/mei/main.c
··· 1060 1060 1061 1061 mutex_lock(&dev->device_lock); 1062 1062 1063 - mei_wd_stop(dev, false); 1063 + cancel_delayed_work(&dev->timer_work); 1064 + 1065 + mei_wd_stop(dev); 1064 1066 1065 1067 mei_device = NULL; 1066 1068 ··· 1117 1115 if (!dev) 1118 1116 return -ENODEV; 1119 1117 mutex_lock(&dev->device_lock); 1118 + 1119 + cancel_delayed_work(&dev->timer_work); 1120 + 1120 1121 /* Stop watchdog if exists */ 1121 - err = mei_wd_stop(dev, true); 1122 + err = mei_wd_stop(dev); 1122 1123 /* Set new mei state */ 1123 1124 if (dev->dev_state == MEI_DEV_ENABLED || 1124 1125 dev->dev_state == MEI_DEV_RECOVERING_FROM_RESET) {
+10 -4
drivers/misc/mei/mei_dev.h
··· 33 33 #define MEI_WD_MIN_TIMEOUT 120 /* seconds */ 34 34 #define MEI_WD_MAX_TIMEOUT 65535 /* seconds */ 35 35 36 + #define MEI_WD_STOP_TIMEOUT 10 /* msecs */ 37 + 36 38 #define MEI_WD_STATE_INDEPENDENCE_MSG_SENT (1 << 0) 37 39 38 40 #define MEI_RD_MSG_BUF_SIZE (128 * sizeof(u32)) ··· 120 118 MEI_FLOW_CONTROL, 121 119 MEI_READING, 122 120 MEI_READ_COMPLETE 121 + }; 122 + 123 + enum mei_wd_states { 124 + MEI_WD_IDLE, 125 + MEI_WD_RUNNING, 126 + MEI_WD_STOPPING, 123 127 }; 124 128 125 129 /* MEI CB */ ··· 236 228 enum mei_dev_state dev_state; 237 229 enum mei_init_clients_states init_clients_state; 238 230 u16 init_clients_timer; 239 - bool stop; 240 231 bool need_reset; 241 232 242 233 u32 extra_write_index; ··· 255 248 bool mei_host_buffer_is_empty; 256 249 257 250 struct mei_cl wd_cl; 251 + enum mei_wd_states wd_state; 258 252 bool wd_interface_reg; 259 253 bool wd_pending; 260 - bool wd_stopped; 261 - bool wd_bypass; /* if false, don't refresh watchdog ME client */ 262 - u16 wd_timeout; /* seconds ((wd_data[1] << 8) + wd_data[0]) */ 254 + u16 wd_timeout; 263 255 unsigned char wd_data[MEI_WD_START_MSG_SIZE]; 264 256 265 257
+13 -13
drivers/misc/mei/wd.c
··· 67 67 /* look for WD client and connect to it */ 68 68 dev->wd_cl.state = MEI_FILE_DISCONNECTED; 69 69 dev->wd_timeout = MEI_WD_DEFAULT_TIMEOUT; 70 + dev->wd_state = MEI_WD_IDLE; 70 71 71 72 /* find ME WD client */ 72 73 mei_me_cl_update_filext(dev, &dev->wd_cl, ··· 129 128 * -EIO when message send fails 130 129 * -EINVAL when invalid message is to be sent 131 130 */ 132 - int mei_wd_stop(struct mei_device *dev, bool preserve) 131 + int mei_wd_stop(struct mei_device *dev) 133 132 { 134 133 int ret; 135 - u16 wd_timeout = dev->wd_timeout; 136 134 137 - cancel_delayed_work(&dev->timer_work); 138 - if (dev->wd_cl.state != MEI_FILE_CONNECTED || !dev->wd_timeout) 135 + if (dev->wd_cl.state != MEI_FILE_CONNECTED || 136 + dev->wd_state != MEI_WD_RUNNING) 139 137 return 0; 140 138 141 - dev->wd_timeout = 0; 142 139 memcpy(dev->wd_data, mei_stop_wd_params, MEI_WD_STOP_MSG_SIZE); 143 - dev->stop = true; 140 + 141 + dev->wd_state = MEI_WD_STOPPING; 144 142 145 143 ret = mei_flow_ctrl_creds(dev, &dev->wd_cl); 146 144 if (ret < 0) ··· 161 161 } else { 162 162 dev->wd_pending = true; 163 163 } 164 - dev->wd_stopped = false; 164 + 165 165 mutex_unlock(&dev->device_lock); 166 166 167 167 ret = wait_event_interruptible_timeout(dev->wait_stop_wd, 168 - dev->wd_stopped, 10 * HZ); 168 + dev->wd_state == MEI_WD_IDLE, 169 + msecs_to_jiffies(MEI_WD_STOP_TIMEOUT)); 169 170 mutex_lock(&dev->device_lock); 170 - if (dev->wd_stopped) { 171 + if (dev->wd_state == MEI_WD_IDLE) { 171 172 dev_dbg(&dev->pdev->dev, "wd: stop completed ret=%d.\n", ret); 172 173 ret = 0; 173 174 } else { ··· 177 176 dev_warn(&dev->pdev->dev, 178 177 "wd: stop failed to complete ret=%d.\n", ret); 179 178 } 180 - 181 - if (preserve) 182 - dev->wd_timeout = wd_timeout; 183 179 184 180 out: 185 181 return ret; ··· 237 239 return -ENODEV; 238 240 239 241 mutex_lock(&dev->device_lock); 240 - mei_wd_stop(dev, false); 242 + mei_wd_stop(dev); 241 243 mutex_unlock(&dev->device_lock); 242 244 243 245 return 0; ··· 266 268 ret = -ENODEV; 267 269 goto end; 268 270 } 271 + 272 + dev->wd_state = MEI_WD_RUNNING; 269 273 270 274 /* Check if we can send the ping to HW*/ 271 275 if (dev->mei_host_buffer_is_empty &&