aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/xen-netback
diff options
context:
space:
mode:
authorPaul Durrant <Paul.Durrant@citrix.com>2013-09-26 07:09:52 -0400
committerDavid S. Miller <davem@davemloft.net>2013-09-30 15:13:28 -0400
commitea732dff5cfa10789007bf4a5b935388a0bb2a8f (patch)
tree9640a44e7b2a9910a5fd9e098a1c3aa7858c41d5 /drivers/net/xen-netback
parentc9d55d5bff05084b5829f751aebd03d0c8f632f5 (diff)
xen-netback: Handle backend state transitions in a more robust way
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>
Diffstat (limited to 'drivers/net/xen-netback')
-rw-r--r--drivers/net/xen-netback/xenbus.c148
1 files changed, 118 insertions, 30 deletions
diff --git a/drivers/net/xen-netback/xenbus.c b/drivers/net/xen-netback/xenbus.c
index a53782ef1540..b45bce20ad76 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;
@@ -136,6 +142,8 @@ static int netback_probe(struct xenbus_device *dev,
136 if (err) 142 if (err)
137 goto fail; 143 goto fail;
138 144
145 be->state = XenbusStateInitWait;
146
139 /* This kicks hotplug scripts, so do it immediately. */ 147 /* This kicks hotplug scripts, so do it immediately. */
140 backend_create_xenvif(be); 148 backend_create_xenvif(be);
141 149
@@ -208,24 +216,113 @@ static void backend_create_xenvif(struct backend_info *be)
208 kobject_uevent(&dev->dev.kobj, KOBJ_ONLINE); 216 kobject_uevent(&dev->dev.kobj, KOBJ_ONLINE);
209} 217}
210 218
211 219static void backend_disconnect(struct backend_info *be)
212static void disconnect_backend(struct xenbus_device *dev)
213{ 220{
214 struct backend_info *be = dev_get_drvdata(&dev->dev);
215
216 if (be->vif) 221 if (be->vif)
217 xenvif_disconnect(be->vif); 222 xenvif_disconnect(be->vif);
218} 223}
219 224
220static void destroy_backend(struct xenbus_device *dev) 225static void backend_connect(struct backend_info *be)
221{ 226{
222 struct backend_info *be = dev_get_drvdata(&dev->dev); 227 if (be->vif)
228 connect(be);
229}
223 230
224 if (be->vif) { 231static inline void backend_switch_state(struct backend_info *be,
225 kobject_uevent(&dev->dev.kobj, KOBJ_OFFLINE); 232 enum xenbus_state state)
226 xenbus_rm(XBT_NIL, dev->nodename, "hotplug-status"); 233{
227 xenvif_free(be->vif); 234 struct xenbus_device *dev = be->dev;
228 be->vif = NULL; 235
236 pr_debug("%s -> %s\n", dev->nodename, xenbus_strstate(state));
237 be->state = state;
238
239 /* If we are waiting for a hotplug script then defer the
240 * actual xenbus state change.
241 */
242 if (!be->have_hotplug_status_watch)
243 xenbus_switch_state(dev, state);
244}
245
246/* Handle backend state transitions:
247 *
248 * The backend state starts in InitWait and the following transitions are
249 * allowed.
250 *
251 * InitWait -> Connected
252 *
253 * ^ \ |
254 * | \ |
255 * | \ |
256 * | \ |
257 * | \ |
258 * | \ |
259 * | V V
260 *
261 * Closed <-> Closing
262 *
263 * The state argument specifies the eventual state of the backend and the
264 * function transitions to that state via the shortest path.
265 */
266static void set_backend_state(struct backend_info *be,
267 enum xenbus_state state)
268{
269 while (be->state != state) {
270 switch (be->state) {
271 case XenbusStateClosed:
272 switch (state) {
273 case XenbusStateInitWait:
274 case XenbusStateConnected:
275 pr_info("%s: prepare for reconnect\n",
276 be->dev->nodename);
277 backend_switch_state(be, XenbusStateInitWait);
278 break;
279 case XenbusStateClosing:
280 backend_switch_state(be, XenbusStateClosing);
281 break;
282 default:
283 BUG();
284 }
285 break;
286 case XenbusStateInitWait:
287 switch (state) {
288 case XenbusStateConnected:
289 backend_connect(be);
290 backend_switch_state(be, XenbusStateConnected);
291 break;
292 case XenbusStateClosing:
293 case XenbusStateClosed:
294 backend_switch_state(be, XenbusStateClosing);
295 break;
296 default:
297 BUG();
298 }
299 break;
300 case XenbusStateConnected:
301 switch (state) {
302 case XenbusStateInitWait:
303 case XenbusStateClosing:
304 case XenbusStateClosed:
305 backend_disconnect(be);
306 backend_switch_state(be, XenbusStateClosing);
307 break;
308 default:
309 BUG();
310 }
311 break;
312 case XenbusStateClosing:
313 switch (state) {
314 case XenbusStateInitWait:
315 case XenbusStateConnected:
316 case XenbusStateClosed:
317 backend_switch_state(be, XenbusStateClosed);
318 break;
319 default:
320 BUG();
321 }
322 break;
323 default:
324 BUG();
325 }
229 } 326 }
230} 327}
231 328
@@ -237,40 +334,33 @@ static void frontend_changed(struct xenbus_device *dev,
237{ 334{
238 struct backend_info *be = dev_get_drvdata(&dev->dev); 335 struct backend_info *be = dev_get_drvdata(&dev->dev);
239 336
240 pr_debug("frontend state %s\n", xenbus_strstate(frontend_state)); 337 pr_debug("%s -> %s\n", dev->otherend, xenbus_strstate(frontend_state));
241 338
242 be->frontend_state = frontend_state; 339 be->frontend_state = frontend_state;
243 340
244 switch (frontend_state) { 341 switch (frontend_state) {
245 case XenbusStateInitialising: 342 case XenbusStateInitialising:
246 if (dev->state == XenbusStateClosed) { 343 set_backend_state(be, XenbusStateInitWait);
247 pr_info("%s: prepare for reconnect\n", dev->nodename);
248 xenbus_switch_state(dev, XenbusStateInitWait);
249 }
250 break; 344 break;
251 345
252 case XenbusStateInitialised: 346 case XenbusStateInitialised:
253 break; 347 break;
254 348
255 case XenbusStateConnected: 349 case XenbusStateConnected:
256 if (dev->state == XenbusStateConnected) 350 set_backend_state(be, XenbusStateConnected);
257 break;
258 if (be->vif)
259 connect(be);
260 break; 351 break;
261 352
262 case XenbusStateClosing: 353 case XenbusStateClosing:
263 disconnect_backend(dev); 354 set_backend_state(be, XenbusStateClosing);
264 xenbus_switch_state(dev, XenbusStateClosing);
265 break; 355 break;
266 356
267 case XenbusStateClosed: 357 case XenbusStateClosed:
268 xenbus_switch_state(dev, XenbusStateClosed); 358 set_backend_state(be, XenbusStateClosed);
269 if (xenbus_dev_is_online(dev)) 359 if (xenbus_dev_is_online(dev))
270 break; 360 break;
271 destroy_backend(dev);
272 /* fall through if not online */ 361 /* fall through if not online */
273 case XenbusStateUnknown: 362 case XenbusStateUnknown:
363 set_backend_state(be, XenbusStateClosed);
274 device_unregister(&dev->dev); 364 device_unregister(&dev->dev);
275 break; 365 break;
276 366
@@ -363,7 +453,9 @@ static void hotplug_status_changed(struct xenbus_watch *watch,
363 if (IS_ERR(str)) 453 if (IS_ERR(str))
364 return; 454 return;
365 if (len == sizeof("connected")-1 && !memcmp(str, "connected", len)) { 455 if (len == sizeof("connected")-1 && !memcmp(str, "connected", len)) {
366 xenbus_switch_state(be->dev, XenbusStateConnected); 456 /* Complete any pending state change */
457 xenbus_switch_state(be->dev, be->state);
458
367 /* Not interested in this watch anymore. */ 459 /* Not interested in this watch anymore. */
368 unregister_hotplug_status_watch(be); 460 unregister_hotplug_status_watch(be);
369 } 461 }
@@ -393,12 +485,8 @@ static void connect(struct backend_info *be)
393 err = xenbus_watch_pathfmt(dev, &be->hotplug_status_watch, 485 err = xenbus_watch_pathfmt(dev, &be->hotplug_status_watch,
394 hotplug_status_changed, 486 hotplug_status_changed,
395 "%s/%s", dev->nodename, "hotplug-status"); 487 "%s/%s", dev->nodename, "hotplug-status");
396 if (err) { 488 if (!err)
397 /* Switch now, since we can't do a watch. */
398 xenbus_switch_state(dev, XenbusStateConnected);
399 } else {
400 be->have_hotplug_status_watch = 1; 489 be->have_hotplug_status_watch = 1;
401 }
402 490
403 netif_wake_queue(be->vif->dev); 491 netif_wake_queue(be->vif->dev);
404} 492}