diff options
-rw-r--r-- | drivers/net/xen-netback/xenbus.c | 149 |
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 @@ | |||
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; |
@@ -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 | 209 | static void backend_disconnect(struct backend_info *be) | |
202 | static 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 | ||
210 | static void destroy_backend(struct xenbus_device *dev) | 215 | static 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) { | 221 | static 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 | */ | ||
256 | static 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 | } |