aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/xen-netback/xenbus.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/xen-netback/xenbus.c')
-rw-r--r--drivers/net/xen-netback/xenbus.c151
1 files changed, 123 insertions, 28 deletions
diff --git a/drivers/net/xen-netback/xenbus.c b/drivers/net/xen-netback/xenbus.c
index 1fe48fe364ed..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;
@@ -42,7 +48,7 @@ static int netback_remove(struct xenbus_device *dev)
42 if (be->vif) { 48 if (be->vif) {
43 kobject_uevent(&dev->dev.kobj, KOBJ_OFFLINE); 49 kobject_uevent(&dev->dev.kobj, KOBJ_OFFLINE);
44 xenbus_rm(XBT_NIL, dev->nodename, "hotplug-status"); 50 xenbus_rm(XBT_NIL, dev->nodename, "hotplug-status");
45 xenvif_disconnect(be->vif); 51 xenvif_free(be->vif);
46 be->vif = NULL; 52 be->vif = NULL;
47 } 53 }
48 kfree(be); 54 kfree(be);
@@ -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,15 +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
219static void backend_disconnect(struct backend_info *be)
220{
221 if (be->vif)
222 xenvif_disconnect(be->vif);
223}
211 224
212static void disconnect_backend(struct xenbus_device *dev) 225static void backend_connect(struct backend_info *be)
213{ 226{
214 struct backend_info *be = dev_get_drvdata(&dev->dev); 227 if (be->vif)
228 connect(be);
229}
215 230
216 if (be->vif) { 231static inline void backend_switch_state(struct backend_info *be,
217 xenbus_rm(XBT_NIL, dev->nodename, "hotplug-status"); 232 enum xenbus_state state)
218 xenvif_disconnect(be->vif); 233{
219 be->vif = NULL; 234 struct xenbus_device *dev = be->dev;
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 }
220 } 326 }
221} 327}
222 328
@@ -228,42 +334,33 @@ static void frontend_changed(struct xenbus_device *dev,
228{ 334{
229 struct backend_info *be = dev_get_drvdata(&dev->dev); 335 struct backend_info *be = dev_get_drvdata(&dev->dev);
230 336
231 pr_debug("frontend state %s\n", xenbus_strstate(frontend_state)); 337 pr_debug("%s -> %s\n", dev->otherend, xenbus_strstate(frontend_state));
232 338
233 be->frontend_state = frontend_state; 339 be->frontend_state = frontend_state;
234 340
235 switch (frontend_state) { 341 switch (frontend_state) {
236 case XenbusStateInitialising: 342 case XenbusStateInitialising:
237 if (dev->state == XenbusStateClosed) { 343 set_backend_state(be, XenbusStateInitWait);
238 pr_info("%s: prepare for reconnect\n", dev->nodename);
239 xenbus_switch_state(dev, XenbusStateInitWait);
240 }
241 break; 344 break;
242 345
243 case XenbusStateInitialised: 346 case XenbusStateInitialised:
244 break; 347 break;
245 348
246 case XenbusStateConnected: 349 case XenbusStateConnected:
247 if (dev->state == XenbusStateConnected) 350 set_backend_state(be, XenbusStateConnected);
248 break;
249 backend_create_xenvif(be);
250 if (be->vif)
251 connect(be);
252 break; 351 break;
253 352
254 case XenbusStateClosing: 353 case XenbusStateClosing:
255 if (be->vif) 354 set_backend_state(be, XenbusStateClosing);
256 kobject_uevent(&dev->dev.kobj, KOBJ_OFFLINE);
257 disconnect_backend(dev);
258 xenbus_switch_state(dev, XenbusStateClosing);
259 break; 355 break;
260 356
261 case XenbusStateClosed: 357 case XenbusStateClosed:
262 xenbus_switch_state(dev, XenbusStateClosed); 358 set_backend_state(be, XenbusStateClosed);
263 if (xenbus_dev_is_online(dev)) 359 if (xenbus_dev_is_online(dev))
264 break; 360 break;
265 /* fall through if not online */ 361 /* fall through if not online */
266 case XenbusStateUnknown: 362 case XenbusStateUnknown:
363 set_backend_state(be, XenbusStateClosed);
267 device_unregister(&dev->dev); 364 device_unregister(&dev->dev);
268 break; 365 break;
269 366
@@ -356,7 +453,9 @@ static void hotplug_status_changed(struct xenbus_watch *watch,
356 if (IS_ERR(str)) 453 if (IS_ERR(str))
357 return; 454 return;
358 if (len == sizeof("connected")-1 && !memcmp(str, "connected", len)) { 455 if (len == sizeof("connected")-1 && !memcmp(str, "connected", len)) {
359 xenbus_switch_state(be->dev, XenbusStateConnected); 456 /* Complete any pending state change */
457 xenbus_switch_state(be->dev, be->state);
458
360 /* Not interested in this watch anymore. */ 459 /* Not interested in this watch anymore. */
361 unregister_hotplug_status_watch(be); 460 unregister_hotplug_status_watch(be);
362 } 461 }
@@ -386,12 +485,8 @@ static void connect(struct backend_info *be)
386 err = xenbus_watch_pathfmt(dev, &be->hotplug_status_watch, 485 err = xenbus_watch_pathfmt(dev, &be->hotplug_status_watch,
387 hotplug_status_changed, 486 hotplug_status_changed,
388 "%s/%s", dev->nodename, "hotplug-status"); 487 "%s/%s", dev->nodename, "hotplug-status");
389 if (err) { 488 if (!err)
390 /* Switch now, since we can't do a watch. */
391 xenbus_switch_state(dev, XenbusStateConnected);
392 } else {
393 be->have_hotplug_status_watch = 1; 489 be->have_hotplug_status_watch = 1;
394 }
395 490
396 netif_wake_queue(be->vif->dev); 491 netif_wake_queue(be->vif->dev);
397} 492}