aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Durrant <Paul.Durrant@citrix.com>2013-09-26 07:09:52 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-11-20 15:27:46 -0500
commit7ab12337112dce7669c2481f6077b1ebfb048ddc (patch)
treeca51d45f13bce4bdb1a1111827bdea35b080b934
parent5bf906573cbe62fb4b4bb61db5086e62c62d9b2a (diff)
xen-netback: Handle backend state transitions in a more robust way
[ Upstream commit ea732dff5cfa10789007bf4a5b935388a0bb2a8f ] When the frontend state changes netback now specifies its desired state to a new function, set_backend_state(), which transitions through any necessary intermediate states. This fixes an issue observed with some old Windows frontend drivers where they failed to transition through the Closing state and netback would not behave correctly. Signed-off-by: Paul Durrant <paul.durrant@citrix.com> Cc: Ian Campbell <ian.campbell@citrix.com> Cc: Wei Liu <wei.liu2@citrix.com> Cc: David Vrabel <david.vrabel@citrix.com> Acked-by: Ian Campbell <ian.campbell@citrix.com> Signed-off-by: David S. Miller <davem@davemloft.net> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/net/xen-netback/xenbus.c149
1 files changed, 118 insertions, 31 deletions
diff --git a/drivers/net/xen-netback/xenbus.c b/drivers/net/xen-netback/xenbus.c
index abe24ff000f0..805a0a328391 100644
--- a/drivers/net/xen-netback/xenbus.c
+++ b/drivers/net/xen-netback/xenbus.c
@@ -24,6 +24,12 @@
24struct backend_info { 24struct backend_info {
25 struct xenbus_device *dev; 25 struct xenbus_device *dev;
26 struct xenvif *vif; 26 struct xenvif *vif;
27
28 /* This is the state that will be reflected in xenstore when any
29 * active hotplug script completes.
30 */
31 enum xenbus_state state;
32
27 enum xenbus_state frontend_state; 33 enum xenbus_state frontend_state;
28 struct xenbus_watch hotplug_status_watch; 34 struct xenbus_watch hotplug_status_watch;
29 u8 have_hotplug_status_watch:1; 35 u8 have_hotplug_status_watch:1;
@@ -126,6 +132,8 @@ static int netback_probe(struct xenbus_device *dev,
126 if (err) 132 if (err)
127 goto fail; 133 goto fail;
128 134
135 be->state = XenbusStateInitWait;
136
129 /* This kicks hotplug scripts, so do it immediately. */ 137 /* This kicks hotplug scripts, so do it immediately. */
130 backend_create_xenvif(be); 138 backend_create_xenvif(be);
131 139
@@ -198,24 +206,113 @@ static void backend_create_xenvif(struct backend_info *be)
198 kobject_uevent(&dev->dev.kobj, KOBJ_ONLINE); 206 kobject_uevent(&dev->dev.kobj, KOBJ_ONLINE);
199} 207}
200 208
201 209static void backend_disconnect(struct backend_info *be)
202static void disconnect_backend(struct xenbus_device *dev)
203{ 210{
204 struct backend_info *be = dev_get_drvdata(&dev->dev);
205
206 if (be->vif) 211 if (be->vif)
207 xenvif_disconnect(be->vif); 212 xenvif_disconnect(be->vif);
208} 213}
209 214
210static void destroy_backend(struct xenbus_device *dev) 215static void backend_connect(struct backend_info *be)
211{ 216{
212 struct backend_info *be = dev_get_drvdata(&dev->dev); 217 if (be->vif)
218 connect(be);
219}
213 220
214 if (be->vif) { 221static inline void backend_switch_state(struct backend_info *be,
215 kobject_uevent(&dev->dev.kobj, KOBJ_OFFLINE); 222 enum xenbus_state state)
216 xenbus_rm(XBT_NIL, dev->nodename, "hotplug-status"); 223{
217 xenvif_free(be->vif); 224 struct xenbus_device *dev = be->dev;
218 be->vif = NULL; 225
226 pr_debug("%s -> %s\n", dev->nodename, xenbus_strstate(state));
227 be->state = state;
228
229 /* If we are waiting for a hotplug script then defer the
230 * actual xenbus state change.
231 */
232 if (!be->have_hotplug_status_watch)
233 xenbus_switch_state(dev, state);
234}
235
236/* Handle backend state transitions:
237 *
238 * The backend state starts in InitWait and the following transitions are
239 * allowed.
240 *
241 * InitWait -> Connected
242 *
243 * ^ \ |
244 * | \ |
245 * | \ |
246 * | \ |
247 * | \ |
248 * | \ |
249 * | V V
250 *
251 * Closed <-> Closing
252 *
253 * The state argument specifies the eventual state of the backend and the
254 * function transitions to that state via the shortest path.
255 */
256static void set_backend_state(struct backend_info *be,
257 enum xenbus_state state)
258{
259 while (be->state != state) {
260 switch (be->state) {
261 case XenbusStateClosed:
262 switch (state) {
263 case XenbusStateInitWait:
264 case XenbusStateConnected:
265 pr_info("%s: prepare for reconnect\n",
266 be->dev->nodename);
267 backend_switch_state(be, XenbusStateInitWait);
268 break;
269 case XenbusStateClosing:
270 backend_switch_state(be, XenbusStateClosing);
271 break;
272 default:
273 BUG();
274 }
275 break;
276 case XenbusStateInitWait:
277 switch (state) {
278 case XenbusStateConnected:
279 backend_connect(be);
280 backend_switch_state(be, XenbusStateConnected);
281 break;
282 case XenbusStateClosing:
283 case XenbusStateClosed:
284 backend_switch_state(be, XenbusStateClosing);
285 break;
286 default:
287 BUG();
288 }
289 break;
290 case XenbusStateConnected:
291 switch (state) {
292 case XenbusStateInitWait:
293 case XenbusStateClosing:
294 case XenbusStateClosed:
295 backend_disconnect(be);
296 backend_switch_state(be, XenbusStateClosing);
297 break;
298 default:
299 BUG();
300 }
301 break;
302 case XenbusStateClosing:
303 switch (state) {
304 case XenbusStateInitWait:
305 case XenbusStateConnected:
306 case XenbusStateClosed:
307 backend_switch_state(be, XenbusStateClosed);
308 break;
309 default:
310 BUG();
311 }
312 break;
313 default:
314 BUG();
315 }
219 } 316 }
220} 317}
221 318
@@ -227,41 +324,33 @@ static void frontend_changed(struct xenbus_device *dev,
227{ 324{
228 struct backend_info *be = dev_get_drvdata(&dev->dev); 325 struct backend_info *be = dev_get_drvdata(&dev->dev);
229 326
230 pr_debug("frontend state %s", xenbus_strstate(frontend_state)); 327 pr_debug("%s -> %s\n", dev->otherend, xenbus_strstate(frontend_state));
231 328
232 be->frontend_state = frontend_state; 329 be->frontend_state = frontend_state;
233 330
234 switch (frontend_state) { 331 switch (frontend_state) {
235 case XenbusStateInitialising: 332 case XenbusStateInitialising:
236 if (dev->state == XenbusStateClosed) { 333 set_backend_state(be, XenbusStateInitWait);
237 printk(KERN_INFO "%s: %s: prepare for reconnect\n",
238 __func__, dev->nodename);
239 xenbus_switch_state(dev, XenbusStateInitWait);
240 }
241 break; 334 break;
242 335
243 case XenbusStateInitialised: 336 case XenbusStateInitialised:
244 break; 337 break;
245 338
246 case XenbusStateConnected: 339 case XenbusStateConnected:
247 if (dev->state == XenbusStateConnected) 340 set_backend_state(be, XenbusStateConnected);
248 break;
249 if (be->vif)
250 connect(be);
251 break; 341 break;
252 342
253 case XenbusStateClosing: 343 case XenbusStateClosing:
254 disconnect_backend(dev); 344 set_backend_state(be, XenbusStateClosing);
255 xenbus_switch_state(dev, XenbusStateClosing);
256 break; 345 break;
257 346
258 case XenbusStateClosed: 347 case XenbusStateClosed:
259 xenbus_switch_state(dev, XenbusStateClosed); 348 set_backend_state(be, XenbusStateClosed);
260 if (xenbus_dev_is_online(dev)) 349 if (xenbus_dev_is_online(dev))
261 break; 350 break;
262 destroy_backend(dev);
263 /* fall through if not online */ 351 /* fall through if not online */
264 case XenbusStateUnknown: 352 case XenbusStateUnknown:
353 set_backend_state(be, XenbusStateClosed);
265 device_unregister(&dev->dev); 354 device_unregister(&dev->dev);
266 break; 355 break;
267 356
@@ -354,7 +443,9 @@ static void hotplug_status_changed(struct xenbus_watch *watch,
354 if (IS_ERR(str)) 443 if (IS_ERR(str))
355 return; 444 return;
356 if (len == sizeof("connected")-1 && !memcmp(str, "connected", len)) { 445 if (len == sizeof("connected")-1 && !memcmp(str, "connected", len)) {
357 xenbus_switch_state(be->dev, XenbusStateConnected); 446 /* Complete any pending state change */
447 xenbus_switch_state(be->dev, be->state);
448
358 /* Not interested in this watch anymore. */ 449 /* Not interested in this watch anymore. */
359 unregister_hotplug_status_watch(be); 450 unregister_hotplug_status_watch(be);
360 } 451 }
@@ -384,12 +475,8 @@ static void connect(struct backend_info *be)
384 err = xenbus_watch_pathfmt(dev, &be->hotplug_status_watch, 475 err = xenbus_watch_pathfmt(dev, &be->hotplug_status_watch,
385 hotplug_status_changed, 476 hotplug_status_changed,
386 "%s/%s", dev->nodename, "hotplug-status"); 477 "%s/%s", dev->nodename, "hotplug-status");
387 if (err) { 478 if (!err)
388 /* Switch now, since we can't do a watch. */
389 xenbus_switch_state(dev, XenbusStateConnected);
390 } else {
391 be->have_hotplug_status_watch = 1; 479 be->have_hotplug_status_watch = 1;
392 }
393 480
394 netif_wake_queue(be->vif->dev); 481 netif_wake_queue(be->vif->dev);
395} 482}