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.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}