diff options
Diffstat (limited to 'drivers/net/xen-netback/xenbus.c')
-rw-r--r-- | drivers/net/xen-netback/xenbus.c | 151 |
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 @@ | |||
24 | struct backend_info { | 24 | struct 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 | ||
219 | static void backend_disconnect(struct backend_info *be) | ||
220 | { | ||
221 | if (be->vif) | ||
222 | xenvif_disconnect(be->vif); | ||
223 | } | ||
211 | 224 | ||
212 | static void disconnect_backend(struct xenbus_device *dev) | 225 | static 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) { | 231 | static 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 | */ | ||
266 | static 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 | } |