LCOV - code coverage report
Current view: top level - tapback - backend.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 478 0.0 %
Date: 2025-04-18 11:59:49 Functions: 0 17 0.0 %
Branches: 0 0 -

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * Copyright (c) 2016, Citrix Systems, Inc.
       3                 :            :  *
       4                 :            :  * All rights reserved.
       5                 :            :  *
       6                 :            :  * Redistribution and use in source and binary forms, with or without
       7                 :            :  * modification, are permitted provided that the following conditions are met:
       8                 :            :  * 
       9                 :            :  *  1. Redistributions of source code must retain the above copyright
      10                 :            :  *     notice, this list of conditions and the following disclaimer.
      11                 :            :  *  2. Redistributions in binary form must reproduce the above copyright
      12                 :            :  *     notice, this list of conditions and the following disclaimer in the
      13                 :            :  *     documentation and/or other materials provided with the distribution.
      14                 :            :  *  3. Neither the name of the copyright holder nor the names of its 
      15                 :            :  *     contributors may be used to endorse or promote products derived from 
      16                 :            :  *     this software without specific prior written permission.
      17                 :            :  *
      18                 :            :  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
      19                 :            :  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
      20                 :            :  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
      21                 :            :  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
      22                 :            :  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
      23                 :            :  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
      24                 :            :  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
      25                 :            :  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
      26                 :            :  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
      27                 :            :  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
      28                 :            :  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
      29                 :            :  *
      30                 :            :  * This file contains the handler executed when the back-end XenStore path gets
      31                 :            :  * modified.
      32                 :            :  */
      33                 :            : 
      34                 :            : #include "config.h"
      35                 :            : 
      36                 :            : #include "tapback.h"
      37                 :            : #include "xenstore.h"
      38                 :            : #include <xen/io/blkif.h>
      39                 :            : #include <unistd.h>
      40                 :            : #include <signal.h>
      41                 :            : #include <stdlib.h>
      42                 :            : #include "xen_blkif.h"
      43                 :            : 
      44                 :            : extern int tapdev_major;
      45                 :            : 
      46                 :            : /**
      47                 :            :  * Removes the XenStore watch from the front-end.
      48                 :            :  *
      49                 :            :  * @param device the VBD whose front-end XenStore path should stop being
      50                 :            :  * watched
      51                 :            :  */
      52                 :            : static void
      53                 :          0 : tapback_device_unwatch_frontend_state(vbd_t * const device)
      54                 :            : {
      55                 :          0 :     ASSERT(device);
      56                 :            : 
      57                 :          0 :     if (device->frontend_state_path)
      58                 :            :                 /* TODO check return code */
      59                 :          0 :         xs_unwatch(device->backend->xs, device->frontend_state_path,
      60                 :          0 :                 device->backend->frontend_token);
      61                 :            : 
      62                 :          0 :     free(device->frontend_state_path);
      63                 :          0 :     device->frontend_state_path = NULL;
      64                 :          0 : }
      65                 :            : 
      66                 :            : /**
      67                 :            :  * Destroys and deallocates the back-end part of a VBD.
      68                 :            :  *
      69                 :            :  * @param device the VBD to destroy
      70                 :            :  * @returns 0 on success, -errno on failure.
      71                 :            :  */
      72                 :            : static int
      73                 :          0 : tapback_backend_destroy_device(vbd_t * const device)
      74                 :            : {
      75                 :            :         int err;
      76                 :            : 
      77                 :          0 :     ASSERT(device);
      78                 :            : 
      79                 :          0 :     DBG(device, "removing VBD\n");
      80                 :            : 
      81                 :          0 :         if (device->tap && device->connected) {
      82                 :            : 
      83                 :          0 :                 DBG(device, "implicitly disconnecting tapdisk[%d] minor=%d from the "
      84                 :            :                                 "ring\n", device->tap->pid, device->minor);
      85                 :            : 
      86                 :          0 :                 err = tap_ctl_disconnect_xenblkif(device->tap->pid, device->domid,
      87                 :            :                                 device->devid, NULL);
      88                 :          0 :                 if (err) {
      89                 :          0 :                         if (err == -ESRCH || err == -ENOENT) {
      90                 :            :                                 /*
      91                 :            :                                  * TODO tapdisk might have died without cleaning up, in which
      92                 :            :                                  * case we'll receive an I/O error when trying to talk to it
      93                 :            :                                  * through the socket, maybe search for a process with that
      94                 :            :                                  * PID? Alternatively, we can spawn tapdisks through a daemon
      95                 :            :                                  * which will monitor tapdisks for abrupt deaths and clean up
      96                 :            :                                  * after them (e.g. remove the socket).
      97                 :            :                                  */
      98                 :          0 :                                 INFO(device, "tapdisk[%d] not running\n", device->tap->pid);
      99                 :            :                                 err = 0;
     100                 :            :                         } else {
     101                 :          0 :                                 WARN(device, "cannot disconnect tapdisk[%d] minor=%d from the "
     102                 :            :                                                 "ring: %s\n", device->tap->pid, device->minor,
     103                 :            :                                                 strerror(-err));
     104                 :            :                         }
     105                 :          0 :                         return err;
     106                 :            :                 }
     107                 :            :         }
     108                 :            : 
     109                 :          0 :     list_del(&device->backend_entry);
     110                 :            : 
     111                 :          0 :     tapback_device_unwatch_frontend_state(device);
     112                 :            : 
     113                 :          0 :         free(device->tap);
     114                 :          0 :     free(device->frontend_path);
     115                 :          0 :     free(device->name);
     116                 :          0 :     free(device);
     117                 :            : 
     118                 :          0 :         return 0;
     119                 :            : }
     120                 :            : 
     121                 :            : /**
     122                 :            :  * Retrieves the tapdisk designated to serve this device, storing this
     123                 :            :  * information in the supplied VBD handle.
     124                 :            :  *
     125                 :            :  * @param minor
     126                 :            :  * @param tap output parameter that receives the tapdisk process information.
     127                 :            :  * The parameter is undefined when the function returns a non-zero value.
     128                 :            :  * @returns 0 if a suitable tapdisk is found, ESRCH if no suitable tapdisk is
     129                 :            :  * found, and a negative error code in case of error
     130                 :            :  *
     131                 :            :  * TODO rename function
     132                 :            :  *
     133                 :            :  * XXX Only called by blkback_probe.
     134                 :            :  */
     135                 :            : static inline int
     136                 :          0 : find_tapdisk(const int minor, tap_list_t *tap)
     137                 :            : {
     138                 :          0 :     struct list_head list = LIST_HEAD_INIT(list);
     139                 :            :     tap_list_t *_tap;
     140                 :            :     int err;
     141                 :            : 
     142                 :          0 :     err = tap_ctl_list(&list);
     143                 :          0 :     if (err) {
     144                 :          0 :         WARN(NULL, "error listing tapdisks: %s\n", strerror(-err));
     145                 :            :         goto out;
     146                 :            :     }
     147                 :            : 
     148                 :          0 :     err = -ESRCH;
     149                 :          0 :     if (!list_empty(&list)) {
     150                 :          0 :         tap_list_for_each_entry(_tap, &list) {
     151                 :          0 :             if (_tap->minor == minor) {
     152                 :          0 :                 err = 0;
     153                 :            :                 memcpy(tap, _tap, sizeof(*tap));
     154                 :            :                 break;
     155                 :            :             }
     156                 :            :         }
     157                 :          0 :         tap_ctl_list_free(&list);
     158                 :            :     } else
     159                 :          0 :         DBG(NULL, "no tapdisks\n");
     160                 :            : 
     161                 :            : out:
     162                 :          0 :     return err;
     163                 :            : }
     164                 :            : 
     165                 :            : static inline int
     166                 :          0 : find_tapdisk_by_pid(const pid_t pid, const int minor, tap_list_t *tap)
     167                 :            : {
     168                 :          0 :         struct list_head list = LIST_HEAD_INIT(list);
     169                 :            :         tap_list_t *entry;
     170                 :            :         int err;
     171                 :            : 
     172                 :          0 :         err = tap_ctl_list_pid(pid, &list);
     173                 :          0 :         if (err) {
     174                 :          0 :                 WARN(NULL, "error listing tapdisks: %s\n", strerror(-err));
     175                 :            :                 goto out;
     176                 :            :         }
     177                 :            : 
     178                 :          0 :         err = -ESRCH;
     179                 :          0 :         if (!list_empty(&list)) {
     180                 :          0 :                 tap_list_for_each_entry(entry, &list) {
     181                 :          0 :                         if (entry->minor == minor && entry->pid == pid) {
     182                 :          0 :                                 err = 0;
     183                 :            :                                 memcpy(tap, entry, sizeof(*tap));
     184                 :            :                                 break;
     185                 :            :                         }
     186                 :            :                 }
     187                 :          0 :                 tap_ctl_list_free(&list);
     188                 :            :         } else {
     189                 :          0 :                 DBG(NULL, "no tapdisks\n");
     190                 :            :         }
     191                 :            : 
     192                 :            : out:
     193                 :          0 :         return err;
     194                 :            : }
     195                 :            : 
     196                 :            : /**
     197                 :            :  * Creates a device and adds it to the list of devices.
     198                 :            :  *
     199                 :            :  * Creating the device implies initializing the handle and retrieving all the
     200                 :            :  * information of the tapdisk serving this VBD.
     201                 :            :  *
     202                 :            :  * @param domid the ID of the domain where the VBD is created
     203                 :            :  * @param name the name of the device
     204                 :            :  * @returns the created device, NULL on error, sets errno
     205                 :            :  *
     206                 :            :  * FIXME If the transaction fails with any error code other than EAGAIN, we're
     207                 :            :  * not undoing everything we did in this function.
     208                 :            :  */
     209                 :            : static inline vbd_t*
     210                 :          0 : tapback_backend_create_device(backend_t *backend,
     211                 :            :                 const domid_t domid, const char * const name)
     212                 :            : {
     213                 :          0 :     vbd_t *device = NULL;
     214                 :          0 :     int err = 0;
     215                 :            : 
     216                 :          0 :         ASSERT(backend);
     217                 :          0 :     ASSERT(name);
     218                 :            : 
     219                 :          0 :     DBG(NULL, "%s creating device\n", name);
     220                 :            : 
     221                 :          0 :     if (!(device = calloc(1, sizeof(*device)))) {
     222                 :          0 :         WARN(NULL, "error allocating memory\n");
     223                 :          0 :         err = errno;
     224                 :          0 :         goto out;
     225                 :            :     }
     226                 :            : 
     227                 :          0 :         device->backend = backend;
     228                 :          0 :     list_add_tail(&device->backend_entry,
     229                 :            :             &device->backend->slave.slave.devices);
     230                 :            : 
     231                 :          0 :     device->major = -1;
     232                 :          0 :     device->minor = -1;
     233                 :            : 
     234                 :          0 :         device->mode = false;
     235                 :          0 :         device->cdrom = false;
     236                 :          0 :         device->info = 0;
     237                 :          0 :         device->polling_duration = 0;
     238                 :          0 :         device->polling_idle_threshold = 0;
     239                 :            : 
     240                 :            :     /* TODO check for errors */
     241                 :          0 :     device->devid = atoi(name);
     242                 :            : 
     243                 :          0 :     device->domid = domid;
     244                 :            : 
     245                 :          0 :         device->state = XenbusStateUnknown;
     246                 :          0 :         device->frontend_state = XenbusStateUnknown;
     247                 :            : 
     248                 :          0 :     if (!(device->name = strdup(name))) {
     249                 :          0 :         err = -errno;
     250                 :          0 :         goto out;
     251                 :            :     }
     252                 :            : 
     253                 :            :     /* Enable multi-page rings */
     254                 :          0 :     err = tapback_device_printf(device, XBT_NULL, "max-ring-page-order",
     255                 :            :                                 true, "%d", MAX_RING_PAGE_ORDER);
     256                 :          0 :     if (unlikely(err)) {
     257                 :          0 :         WARN(device, "failed to write max-ring-page-order: %s\n",
     258                 :            :              strerror(-err));
     259                 :            :         goto out;
     260                 :            :     }
     261                 :            : 
     262                 :            : out:
     263                 :          0 :     if (err) {
     264                 :          0 :         WARN(NULL, "%s: error creating device: %s\n", name, strerror(-err));
     265                 :          0 :         if (device) {
     266                 :          0 :             int err2 = tapback_backend_destroy_device(device);
     267                 :          0 :                         if (err2)
     268                 :          0 :                                 WARN(device, "error cleaning up: failed to destroy the "
     269                 :            :                                                 "device: %s\n", strerror(-err2));
     270                 :            :                 }
     271                 :          0 :         device = NULL;
     272                 :          0 :         errno = err;
     273                 :            :     }
     274                 :          0 :     return device;
     275                 :            : }
     276                 :            : 
     277                 :            : static int
     278                 :          0 : device_set_mode(vbd_t *device, const char *mode) {
     279                 :            : 
     280                 :          0 :         ASSERT(device);
     281                 :          0 :         ASSERT(mode);
     282                 :            : 
     283                 :          0 :         if (!strcmp(mode, "r"))
     284                 :          0 :                 device->mode = false;
     285                 :          0 :         else if (!strcmp(mode, "w"))
     286                 :          0 :                 device->mode = true;
     287                 :            :         else {
     288                 :          0 :                 WARN(device, "invalid value %s for XenStore key %s\n",
     289                 :            :                                 mode, MODE_KEY);
     290                 :            :                 return -EINVAL;
     291                 :            :         }
     292                 :            : 
     293                 :            :         return 0;
     294                 :            : }
     295                 :            : 
     296                 :            : int
     297                 :          0 : physical_device_path_changed(vbd_t *device) {
     298                 :          0 :         int err = 0, minor = -1;
     299                 :          0 :         pid_t pid = -1;
     300                 :          0 :         char *s = NULL;
     301                 :            : 
     302                 :            :         /*
     303                 :            :          * Assume there will never be an OS with pid_t larger than long.
     304                 :            :          */
     305                 :          0 :         const char *nbd_pattern = "/run/blktap-control/nbd%ld.%d";
     306                 :          0 :         device->tap = NULL;
     307                 :            : 
     308                 :          0 :         s = tapback_device_read(device, XBT_NULL, PHYS_DEV_PATH_KEY);
     309                 :          0 :         if (!s) {
     310                 :          0 :                 err = -errno;
     311                 :          0 :                 WARN(device, "Failed to read the %s: %s\n",
     312                 :            :                      PHYS_DEV_PATH_KEY, strerror(-err));
     313                 :            :                 goto done;
     314                 :            :         }
     315                 :            : 
     316                 :          0 :         if (sscanf(s, nbd_pattern, &pid, &minor) != 2) {
     317                 :          0 :                 err = -EINVAL;
     318                 :          0 :                 WARN(device, "Malformed %s: '%s'\n", PHYS_DEV_PATH_KEY, s);
     319                 :            :                 goto out;
     320                 :            :         }
     321                 :            : 
     322                 :          0 :         device->tap = malloc(sizeof(*device->tap));
     323                 :          0 :         if (!device->tap) {
     324                 :            :                 err = -ENOMEM;
     325                 :            :                 goto out;
     326                 :            :         }
     327                 :            : 
     328                 :          0 :         err = find_tapdisk_by_pid(pid, minor, device->tap);
     329                 :          0 :         if (err) {
     330                 :          0 :                 WARN(device, "Error looking for tapdisk: %s\n", strerror(-err));
     331                 :            :                 goto out;
     332                 :            :         }
     333                 :          0 :         INFO(device, "Found tapdisk[%d]\n", device->tap->pid);
     334                 :          0 :         device->minor = minor;
     335                 :            : 
     336                 :            :         /*
     337                 :            :          * get the VBD parameters from the tapdisk
     338                 :            :          */
     339                 :          0 :         if ((err = tap_ctl_info(device->tap->pid, &device->sectors,
     340                 :            :                                         &device->sector_size, &device->info,
     341                 :            :                                         device->minor))) {
     342                 :          0 :                 WARN(device, "error retrieving disk characteristics: %s\n",
     343                 :            :                      strerror(-err));
     344                 :            :                 goto out;
     345                 :            :         }
     346                 :            : 
     347                 :          0 :         err = tapback_device_printf(device, XBT_NULL, "kthread-pid", false, "%d",
     348                 :          0 :                 device->tap->pid);
     349                 :          0 :         if (unlikely(err)) {
     350                 :          0 :                 WARN(device, "warning: failed to write kthread-pid: %s\n",
     351                 :            :                      strerror(-err));
     352                 :            :                 goto out;
     353                 :            :         }
     354                 :            : 
     355                 :          0 :         if (device->sector_size & 0x1ff || device->sectors <= 0) {
     356                 :          0 :                 WARN(device, "warning: unexpected device characteristics: sector "
     357                 :            :                      "size=%d, sectors=%llu\n", device->sector_size,
     358                 :            :                      device->sectors);
     359                 :            :         }
     360                 :            : 
     361                 :            :         /*
     362                 :            :          * The front-end might have switched to state Connected before
     363                 :            :          * physical-device is written. Check it's state and connect if necessary.
     364                 :            :          *
     365                 :            :          * TODO blkback ignores connection errors, let's do the same until we
     366                 :            :          * know better.
     367                 :            :          */
     368                 :          0 :         err = -frontend_changed(device, device->frontend_state);
     369                 :          0 :         if (err) {
     370                 :          0 :                 WARN(device, "failed to switch state: %s (error ignored)\n",
     371                 :            :                      strerror(-err));
     372                 :            :         }
     373                 :            :         err = 0;
     374                 :            : out:
     375                 :          0 :         if (err) {
     376                 :          0 :                 free(device->tap);
     377                 :          0 :                 device->tap = NULL;
     378                 :          0 :                 device->sector_size = device->sectors = device->info = 0;
     379                 :            :         }
     380                 :          0 :         free(s);
     381                 :            : done:
     382                 :          0 :         return err;
     383                 :            : }
     384                 :            : 
     385                 :            : /**
     386                 :            :  * Retrieves the minor number and locates the corresponding tapdisk, storing
     387                 :            :  * all relevant information in @device. Then, it attempts to advance the Xenbus
     388                 :            :  * state as it might be that everything is ready and all that was missing was
     389                 :            :  * the physical device.
     390                 :            :  *
     391                 :            :  * We might already have read the major/minor, but if the physical-device
     392                 :            :  * key triggered we need to make sure it hasn't changed. This also protects us
     393                 :            :  * against restarted transactions.
     394                 :            :  *
     395                 :            :  * Returns 0 on success, a negative error code otherwise.
     396                 :            :  */
     397                 :            : static int
     398                 :          0 : physical_device_changed(vbd_t *device) {
     399                 :            : 
     400                 :          0 :     char * s = NULL, *p = NULL, *end = NULL;
     401                 :          0 :     int err = 0, major = 0, minor = 0;
     402                 :            :     unsigned int info;
     403                 :            : 
     404                 :          0 :     ASSERT(device);
     405                 :            : 
     406                 :          0 :     INFO(device, "physical_device_changed\n");
     407                 :            : 
     408                 :            :     /*
     409                 :            :      * Get the minor.
     410                 :            :      */
     411                 :          0 :     s = tapback_device_read(device, XBT_NULL, PHYS_DEV_KEY);
     412                 :          0 :     if (!s) {
     413                 :          0 :         err = -errno;
     414                 :          0 :         if (err != -ENOENT)
     415                 :          0 :             WARN(device, "failed to read the physical-device: %s\n",
     416                 :            :                     strerror(-err));
     417                 :            :         goto out;
     418                 :            :     }
     419                 :            : 
     420                 :          0 :     if (strlen(s) == 0) {
     421                 :            :             /* empty string is a missing device */
     422                 :            :             err = -ENOENT;
     423                 :            :             goto out;
     424                 :            :     }
     425                 :            : 
     426                 :            :     /*
     427                 :            :      * The XenStore key physical-device contains "major:minor" in hex.
     428                 :            :      */
     429                 :          0 :     p = strtok(s, ":");
     430                 :          0 :     if (!p) {
     431                 :          0 :         WARN(device, "malformed physical device '%s'\n", s);
     432                 :            :         err = -EINVAL;
     433                 :            :         goto out;
     434                 :            :     }
     435                 :          0 :     major = strtol(p, &end, 16);
     436                 :          0 :     if (*end != 0 || end == p) {
     437                 :          0 :         WARN(device, "malformed physical device '%s'\n", s);
     438                 :            :         err = -EINVAL;
     439                 :            :         goto out;
     440                 :            :     }
     441                 :          0 :     p = strtok(NULL, ":");
     442                 :          0 :         if (unlikely(!p)) {
     443                 :          0 :         WARN(device, "malformed physical device '%s'\n", s);
     444                 :            :         err = -EINVAL;
     445                 :            :         goto out;
     446                 :            :         }
     447                 :          0 :     minor = strtol(p, &end, 16);
     448                 :          0 :     if (*end != 0 || end == p) {
     449                 :          0 :         WARN(device, "malformed physical device '%s'\n", s);
     450                 :            :         err = -EINVAL;
     451                 :            :         goto out;
     452                 :            :     }
     453                 :            : 
     454                 :          0 :     if ((device->major >= 0 || device->minor >= 0) &&
     455                 :          0 :             (major != device->major || minor != device->minor)) {
     456                 :          0 :         WARN(device, "changing physical device from %x:%x to %x:%x not "
     457                 :            :                 "supported\n", device->major, device->minor, major, minor);
     458                 :            :         err = -ENOSYS;
     459                 :            :         goto out;
     460                 :            :     }
     461                 :            : 
     462                 :          0 :     if (major != tapdev_major) {
     463                 :          0 :         WARN(device, "ignoring non-blktap2 physical device: %d\n", major);
     464                 :            :         err = -EINVAL;
     465                 :            :         goto out;
     466                 :            :     }
     467                 :            : 
     468                 :          0 :     device->major = major;
     469                 :          0 :     device->minor = minor;
     470                 :            : 
     471                 :          0 :     device->tap = malloc(sizeof(*device->tap));
     472                 :          0 :     if (!device->tap) {
     473                 :            :         err = -ENOMEM;
     474                 :            :         goto out;
     475                 :            :     }
     476                 :            : 
     477                 :            :     /*
     478                 :            :      * XXX If the physical-device key has been written we expect a tapdisk to
     479                 :            :      * have been created. If tapdisk is created after the physical-device key
     480                 :            :      * is written we have no way of being notified, so we will not be able to
     481                 :            :      * advance the back-end state.
     482                 :            :      */
     483                 :            : 
     484                 :          0 :     DBG(device, "need to find tapdisk serving minor=%d\n", device->minor);
     485                 :            : 
     486                 :          0 :     err = find_tapdisk(device->minor, device->tap);
     487                 :          0 :     if (err) {
     488                 :          0 :         WARN(device, "error looking for tapdisk: %s\n", strerror(-err));
     489                 :            :         goto out;
     490                 :            :     }
     491                 :            : 
     492                 :          0 :     INFO(device, "found tapdisk[%d], for %d:%d\n", device->tap->pid, device->major, device->minor);
     493                 :            : 
     494                 :            :     /*
     495                 :            :      * get the VBD parameters from the tapdisk
     496                 :            :      */
     497                 :          0 :     if ((err = tap_ctl_info(device->tap->pid, &device->sectors,
     498                 :            :                     &device->sector_size, &info,
     499                 :            :                     device->minor))) {
     500                 :          0 :         WARN(device, "error retrieving disk characteristics: %s\n",
     501                 :            :                 strerror(-err));
     502                 :            :         goto out;
     503                 :            :     }
     504                 :            : 
     505                 :          0 :         err = tapback_device_printf(device, XBT_NULL, "kthread-pid", false, "%d",
     506                 :          0 :                 device->tap->pid);
     507                 :          0 :         if (unlikely(err)) {
     508                 :          0 :                 WARN(device, "warning: failed to write kthread-pid: %s\n",
     509                 :            :                                 strerror(-err));
     510                 :            :                 goto out;
     511                 :            :         }
     512                 :            : 
     513                 :          0 :     if (device->sector_size & 0x1ff || device->sectors <= 0) {
     514                 :          0 :         WARN(device, "warning: unexpected device characteristics: sector "
     515                 :            :                 "size=%d, sectors=%llu\n", device->sector_size,
     516                 :            :                                 device->sectors);
     517                 :            :     }
     518                 :            : 
     519                 :            :     /*
     520                 :            :      * The front-end might have switched to state Connected before
     521                 :            :      * physical-device is written. Check it's state and connect if necessary.
     522                 :            :      *
     523                 :            :      * TODO blkback ignores connection errors, let's do the same until we
     524                 :            :      * know better.
     525                 :            :      */
     526                 :          0 :     err = -frontend_changed(device, device->frontend_state);
     527                 :          0 :     if (err)
     528                 :          0 :         WARN(device, "failed to switch state: %s (error ignored)\n",
     529                 :            :                 strerror(-err));
     530                 :            :     err = 0;
     531                 :            : out:
     532                 :          0 :     if (err) {
     533                 :          0 :         free(device->tap);
     534                 :          0 :         device->tap = NULL;
     535                 :          0 :         device->sector_size = device->sectors = device->info = 0;
     536                 :            :     }
     537                 :          0 :     free(s);
     538                 :          0 :     return err;
     539                 :            : }
     540                 :            : 
     541                 :            : /**
     542                 :            :  * Start watching the front-end state path.
     543                 :            :  */
     544                 :            : static int
     545                 :          0 : frontend(vbd_t *device) {
     546                 :            : 
     547                 :          0 :     int err = 0;
     548                 :            : 
     549                 :          0 :     ASSERT(device);
     550                 :            : 
     551                 :          0 :     if (device->frontend_path)
     552                 :            :         return 0;
     553                 :            : 
     554                 :            :     /*
     555                 :            :      * Get the front-end path from XenStore. We need this to talk to
     556                 :            :      * the front-end.
     557                 :            :      */
     558                 :          0 :     if (!(device->frontend_path = tapback_device_read(device, XBT_NULL,
     559                 :            :                     FRONTEND_KEY))) {
     560                 :          0 :         err = -errno;
     561                 :          0 :         if (err != -ENOENT)
     562                 :          0 :             WARN(device, "failed to read front-end path: %s\n", strerror(-err));
     563                 :            :         goto out;
     564                 :            :     }
     565                 :            : 
     566                 :            :     /*
     567                 :            :      * Watch the front-end path in XenStore for changes, i.e.
     568                 :            :      * /local/domain/<domid>/device/vbd/<devname>/state After this, we
     569                 :            :      * wait for the front-end to switch state to continue with the
     570                 :            :      * initialisation.
     571                 :            :      */
     572                 :          0 :     if (asprintf(&device->frontend_state_path, "%s/state",
     573                 :            :                 device->frontend_path) == -1) {
     574                 :          0 :         err = -errno;
     575                 :          0 :         WARN(device, "failed to asprintf: %s\n", strerror(-err));
     576                 :            :         goto out;
     577                 :            :     }
     578                 :            : 
     579                 :            :     /*
     580                 :            :      * We use the same token for all front-end watches. We don't have
     581                 :            :      * to use a unique token for each front-end watch because when a
     582                 :            :      * front-end watch fires we are given the XenStore path that
     583                 :            :      * changed.
     584                 :            :      */
     585                 :          0 :     if (!xs_watch(device->backend->xs, device->frontend_state_path,
     586                 :          0 :                 device->backend->frontend_token)) {
     587                 :          0 :         err = -errno;
     588                 :          0 :         WARN(device, "failed to watch the front-end path (%s): %s\n",
     589                 :            :                 device->frontend_state_path, strerror(-err));
     590                 :            :         goto out;
     591                 :            :     }
     592                 :            : 
     593                 :            : out:
     594                 :          0 :     if (err) {
     595                 :          0 :         free(device->frontend_path);
     596                 :          0 :         device->frontend_path = NULL;
     597                 :          0 :         free(device->frontend_state_path);
     598                 :          0 :         device->frontend_state_path = NULL;
     599                 :            :     }
     600                 :          0 :     return err;
     601                 :            : }
     602                 :            : 
     603                 :            : /**
     604                 :            :  * Reads the hotplug-status XenStore key from the back-end and attemps to
     605                 :            :  * connect to the front-end.
     606                 :            :  *
     607                 :            :  * FIXME This function REQUIRES the device->frontend_path member to be
     608                 :            :  * populated, and this is done by frontend().
     609                 :            :  *
     610                 :            :  * Connecting to the front-end requires the physical-device key to have been
     611                 :            :  * written. This function will attempt to connect anyway, and connecting will
     612                 :            :  * fail half-way through. This is expected.
     613                 :            :  *
     614                 :            :  * Returns 0 in success, -errno on failure.
     615                 :            :  */
     616                 :            : static int
     617                 :          0 : hotplug_status_changed(vbd_t * const device) {
     618                 :            : 
     619                 :          0 :         int err = 0;
     620                 :          0 :         char *hotplug_status = NULL, *device_type = NULL, *mode = NULL, *polling_duration = NULL, *polling_idle_threshold = NULL;
     621                 :            : 
     622                 :          0 :         ASSERT(device);
     623                 :            : 
     624                 :          0 :         if (unlikely(!device->frontend_path)) {
     625                 :            :                 /*
     626                 :            :                  * Something has gone horribly wrong.
     627                 :            :                  */
     628                 :          0 :                 WARN(device, "hotplug scripts ran but front-end does not exist\n");
     629                 :            :                 err = -EINVAL;
     630                 :            :                 goto out;
     631                 :            :         }
     632                 :            : 
     633                 :          0 :         hotplug_status = tapback_device_read(device, XBT_NULL, HOTPLUG_STATUS_KEY);
     634                 :          0 :         if (!hotplug_status) {
     635                 :          0 :                 err = -errno;
     636                 :          0 :                 goto out;
     637                 :            :         }
     638                 :          0 :         if (!strcmp(hotplug_status, "connected")) {
     639                 :            : 
     640                 :          0 :         DBG(device, "physical device available (hotplug scripts ran)\n");
     641                 :            : 
     642                 :          0 :                 device->hotplug_status_connected = true;
     643                 :            : 
     644                 :          0 :         device_type = tapback_device_read_otherend(device, XBT_NULL,
     645                 :            :                 "device-type");
     646                 :          0 :         if (!device_type) {
     647                 :          0 :             err = -errno;
     648                 :          0 :             WARN(device, "failed to read device-type: %s\n", strerror(-err));
     649                 :            :             goto out;
     650                 :            :         }
     651                 :          0 :         if (!strcmp(device_type, "cdrom"))
     652                 :          0 :             device->cdrom = true;
     653                 :          0 :         else if (!strcmp(device_type, "disk"))
     654                 :          0 :             device->cdrom = false;
     655                 :            :         else {
     656                 :          0 :             WARN(device, "unsupported device type %s\n", device_type);
     657                 :            :             err = -EOPNOTSUPP;
     658                 :            :             goto out;
     659                 :            :         }
     660                 :            : 
     661                 :          0 :         mode = tapback_device_read(device, XBT_NULL, MODE_KEY);
     662                 :          0 :         if (!mode) {
     663                 :          0 :             err = -errno;
     664                 :          0 :             WARN(device, "failed to read XenStore key %s: %s\n",
     665                 :            :                     MODE_KEY, strerror(-err));
     666                 :            :             goto out;
     667                 :            :         }
     668                 :          0 :         err = device_set_mode(device, mode);
     669                 :          0 :         if (err) {
     670                 :          0 :             WARN(device, "failed to set device R/W mode: %s\n",
     671                 :            :                     strerror(-err));
     672                 :            :             goto out;
     673                 :            :         }
     674                 :            : 
     675                 :          0 :         if (device->cdrom)
     676                 :          0 :             device->info |= VDISK_CDROM;
     677                 :          0 :         if (!device->mode)
     678                 :          0 :             device->info |= VDISK_READONLY;
     679                 :            : 
     680                 :            :         /* Set polling duration if the key exists. Otherwise polling duration remains 0, i.e. polling is disabled. */
     681                 :          0 :         polling_duration = tapback_device_read(device, XBT_NULL, POLLING_DURATION);
     682                 :          0 :         if (polling_duration)
     683                 :          0 :             device->polling_duration = atoi(polling_duration);
     684                 :            : 
     685                 :            :         /* Set polling idle threshold if the key exists. Otherwise threshold remains 0, i.e. always permit polling */
     686                 :          0 :         polling_idle_threshold = tapback_device_read(device, XBT_NULL, POLLING_IDLE_THRESHOLD);
     687                 :          0 :         if (polling_idle_threshold)
     688                 :          0 :             device->polling_idle_threshold = atoi(polling_idle_threshold);
     689                 :            : 
     690                 :            :         /*
     691                 :            :          * Attempt to connect as everything may be ready and the only thing the
     692                 :            :          * back-end is waiting for is this XenStore key to be written.
     693                 :            :          */
     694                 :          0 :         err = frontend_changed(device, device->frontend_state);
     695                 :          0 :         if (err) {
     696                 :            :             /*
     697                 :            :              * Ignore connection errors as the front-end might not yet be
     698                 :            :              * ready. blkback doesn't wait for this XenStore key to be written,
     699                 :            :              * so we choose to handle this the same way we do with
     700                 :            :              * physical-device.
     701                 :            :              */
     702                 :          0 :             INFO(device, "failed to connect to the front-end: %s "
     703                 :            :                     "(error ignored)\n", strerror(err));
     704                 :            :         }
     705                 :            :         err = 0;
     706                 :            :         }
     707                 :            :         else {
     708                 :          0 :                 WARN(device, "invalid hotplug-status value %s\n", hotplug_status);
     709                 :            :                 err = -EINVAL;
     710                 :            :         }
     711                 :            : 
     712                 :            : out:
     713                 :          0 :         free(hotplug_status);
     714                 :          0 :     free(device_type);
     715                 :          0 :         free(mode);
     716                 :            : 
     717                 :          0 :         return err;
     718                 :            : }
     719                 :            : 
     720                 :            : /**
     721                 :            :  * Attempts to reconnected the back-end to the fornt-end if possible (e.g.
     722                 :            :  * after a tapback restart), or after the slave tapback has started.
     723                 :            :  *
     724                 :            :  * The order we call the functions is not important, apart from
     725                 :            :  * tapback_backend_handle_otherend_watch that MUST be at the end, because each
     726                 :            :  * function attemps to reconnect but won't do so because the front-end state
     727                 :            :  * won't have been read.
     728                 :            :  *
     729                 :            :  * returns 0 on success, an error code otherwise
     730                 :            :  *
     731                 :            :  * NB that 0 is also returned when a reconnection is not yet feasible
     732                 :            :  */
     733                 :            : static inline int
     734                 :          0 : reconnect(vbd_t *device) {
     735                 :            : 
     736                 :            :     int err;
     737                 :            : 
     738                 :          0 :     DBG(device, "attempting reconnect\n");
     739                 :            : 
     740                 :            :     /*
     741                 :            :      * frontend() must be called before all other functions.
     742                 :            :      */
     743                 :          0 :     err = frontend(device);
     744                 :          0 :     if (err) {
     745                 :            :         /*
     746                 :            :          * tapdisk or the front-end state path are not available.
     747                 :            :          */
     748                 :          0 :         if (err == -ENOENT) {
     749                 :          0 :             DBG(device, "front-end XenStore sub-tree does not yet exist\n");
     750                 :            :             err = 0;
     751                 :          0 :         } else if (err == -ESRCH) {
     752                 :          0 :             DBG(device, "tapdisk not yet available\n");
     753                 :            :             err = 0;
     754                 :            :         } else
     755                 :          0 :             WARN(device, "failed to watch front-end path: %s\n",
     756                 :            :                     strerror(-err));
     757                 :            :         goto out;
     758                 :            :     }
     759                 :            : 
     760                 :          0 :     err = physical_device_changed(device);
     761                 :          0 :     if (err) {
     762                 :          0 :         if (err == -ENOENT) {
     763                 :          0 :             DBG(device, "no physical device yet\n");
     764                 :            :             err = 0;
     765                 :            :         } else {
     766                 :          0 :             WARN(device, "failed to retrieve physical device information: "
     767                 :            :                     "%s\n", strerror(-err));
     768                 :            :             goto out;
     769                 :            :         }
     770                 :            :     }
     771                 :            : 
     772                 :          0 :     err = hotplug_status_changed(device);
     773                 :          0 :     if (err) {
     774                 :          0 :         if (err == -ENOENT) {
     775                 :          0 :             DBG(device, "udev scripts haven't yet run\n");
     776                 :            :             err = 0;
     777                 :            :         } else {
     778                 :          0 :             WARN(device, "failed to retrieve hotplug information: %s\n",
     779                 :            :                     strerror(-err));
     780                 :            :             goto out;
     781                 :            :         }
     782                 :            :     }
     783                 :            : 
     784                 :          0 :     err = -tapback_backend_handle_otherend_watch(device->backend,
     785                 :          0 :                         device->frontend_state_path);
     786                 :          0 :     if (err) {
     787                 :          0 :         if (err == -ENOENT) {
     788                 :          0 :             DBG(device, "front-end not yet ready\n");
     789                 :            :             err = 0;
     790                 :            :         } else
     791                 :          0 :             WARN(device, "error running the Xenbus protocol: %s\n",
     792                 :            :                     strerror(-err));
     793                 :            :     }
     794                 :            : out:
     795                 :          0 :     return err;
     796                 :            : }
     797                 :            : 
     798                 :            : /**
     799                 :            :  * Creates (removes) a device depending on the existence (non-existence) of the
     800                 :            :  * "backend/<backend name>/@domid/@devname" XenStore path.
     801                 :            :  * Also handles some device status change behaviour which is neither create nor
     802                 :            :  * remove.
     803                 :            :  *
     804                 :            :  * @param domid the ID of the domain where the VBD is created
     805                 :            :  * @param devname device name
     806                 :            :  * @param comp the XenStore component after the
     807                 :            :  * backend/<backend name>/@domid/@devname/. Might be NULL if the device just
     808                 :            :  * got created.
     809                 :            :  * @returns 0 on success, a negative error code otherwise
     810                 :            :  */
     811                 :            : static int
     812                 :          0 : tapback_backend_probe_device(backend_t *backend,
     813                 :            :                 const domid_t domid, const char * const devname, const char *comp)
     814                 :            : {
     815                 :          0 :     bool should_exist = false, create = false, remove = false;
     816                 :          0 :     int err = 0;
     817                 :          0 :     vbd_t *device = NULL;
     818                 :          0 :     char * s = NULL;
     819                 :            : 
     820                 :          0 :         ASSERT(backend);
     821                 :          0 :     ASSERT(devname);
     822                 :            : 
     823                 :          0 :     ASSERT(!tapback_is_master(backend));
     824                 :            : 
     825                 :          0 :     DBG(NULL, "%s probing device\n", devname);
     826                 :            : 
     827                 :            :     /*
     828                 :            :      * Ask XenStore if the device _should_ exist.
     829                 :            :      */
     830                 :          0 :     s = tapback_xs_read(backend->xs, XBT_NULL, "%s/%s",
     831                 :            :             backend->path, devname);
     832                 :          0 :     should_exist = s != NULL;
     833                 :          0 :     free(s);
     834                 :            : 
     835                 :            :     /*
     836                 :            :      * Search the device list for this specific device.
     837                 :            :      */
     838                 :          0 :     tapback_backend_find_device(backend, device,
     839                 :            :             device->domid == domid && !strcmp(device->name, devname));
     840                 :            : 
     841                 :            :         /*
     842                 :            :          * If XenStore says that the device should exist but it's not in our device
     843                 :            :          * list, we must create it. If it's the other way round, this is a removal.
     844                 :            :          *
     845                 :            :          * It *is* possible for a device to be not in our device list *and* XenStore
     846                 :            :          * does not say it should exist, in which case neither create nor remove
     847                 :            :          * will be true. This needs to fall through to some additional behaviour
     848                 :            :          * which records status changes.
     849                 :            :         */
     850                 :          0 :     remove = device && !should_exist;
     851                 :          0 :     create = !device && should_exist;
     852                 :            : 
     853                 :            :     /*
     854                 :            :      * Remember that remove and create may both be true at the same time, as
     855                 :            :      * this indicates that the device has been removed and re-created too fast.
     856                 :            :      * (FIXME Is this really true?) In this case, we too need to remove and
     857                 :            :      * re-create the device, respectively.
     858                 :            :      */
     859                 :            : 
     860                 :          0 :     if (remove) {
     861                 :          0 :         err = tapback_backend_destroy_device(device);
     862                 :          0 :                 if (err) {
     863                 :          0 :                         WARN(device, "failed to destroy the device: %s\n", strerror(-err));
     864                 :          0 :                         return err;
     865                 :            :                 }
     866                 :            :                 device = NULL;
     867                 :            :         }
     868                 :            : 
     869                 :          0 :     if (create) {
     870                 :          0 :         device = tapback_backend_create_device(backend, domid, devname);
     871                 :          0 :         if (!device) {
     872                 :          0 :             err = errno;
     873                 :          0 :             WARN(NULL, "%s error creating device: %s\n", devname,
     874                 :            :                     strerror(err));
     875                 :          0 :             return err;
     876                 :            :         }
     877                 :            : 
     878                 :          0 :         err = reconnect(device);
     879                 :          0 :         if (err)
     880                 :          0 :             WARN(device, "failed to reconnect: %s\n", strerror(-err));
     881                 :            :     }
     882                 :            : 
     883                 :            :     /*
     884                 :            :      * Examine what happened to the XenStore component on which the watch
     885                 :            :      * triggered.
     886                 :            :      *
     887                 :            :      * We don't set a XenStore watch on these paths in order to limit the
     888                 :            :      * number of watches for performance reasons.
     889                 :            :      */
     890                 :          0 :     if (device && !remove && comp) {
     891                 :            :         /*
     892                 :            :          * TODO Replace this with a despatch table mapping XenStore keys to
     893                 :            :          * callbacks.
     894                 :            :          *
     895                 :            :          * XXX physical_device_changed() and hotplug_status_changed() require
     896                 :            :          * frontend() to have been called beforehand. This is achieved by
     897                 :            :          * calling reconnect by calling reconnect() when the VBD is created.
     898                 :            :          */
     899                 :          0 :         if (!strcmp(PHYS_DEV_KEY, comp))
     900                 :          0 :             err = physical_device_changed(device);
     901                 :          0 :         if (!strcmp(PHYS_DEV_PATH_KEY, comp))
     902                 :          0 :                 err = physical_device_path_changed(device);
     903                 :          0 :         else if (!strcmp(FRONTEND_KEY, comp))
     904                 :          0 :             err = frontend(device);
     905                 :          0 :                 else if (!strcmp(HOTPLUG_STATUS_KEY, comp))
     906                 :          0 :                         err = hotplug_status_changed(device);
     907                 :            :         else
     908                 :          0 :             DBG(device, "ignoring '%s'\n", comp);
     909                 :            :     }
     910                 :            : 
     911                 :          0 :     if (err && create && device) {
     912                 :          0 :         int err2 = tapback_backend_destroy_device(device);
     913                 :          0 :                 if (err2)
     914                 :          0 :                         WARN(device, "error cleaning up: failed to destroy the device: "
     915                 :            :                                         "%s\n", strerror(-err2));
     916                 :            :         }
     917                 :          0 :     return err;
     918                 :            : }
     919                 :            : 
     920                 :            : /**
     921                 :            :  * Scans XenStore and probes any device found.
     922                 :            :  */
     923                 :            : static int
     924                 :          0 : tapback_domain_scan(backend_t *backend, const domid_t domid)
     925                 :            : {
     926                 :          0 :         char *path = NULL, **sub = NULL;
     927                 :          0 :         int err = 0;
     928                 :          0 :         unsigned i, n = 0;
     929                 :            : 
     930                 :          0 :         ASSERT(backend);
     931                 :            : 
     932                 :          0 :     if (tapback_is_master(backend)) {
     933                 :            :         /*
     934                 :            :          * FIXME The master tapback can reach this only if it has been
     935                 :            :          * restarted. We need to figure out which slaves are running and
     936                 :            :          * reconstruct state.
     937                 :            :          */
     938                 :          0 :         WARN(NULL, "master restart not yet implemented, ignoring domain %d\n",
     939                 :            :                 domid);
     940                 :            :     } else {
     941                 :            :         /*
     942                 :            :          * Read the devices of this domain.
     943                 :            :          */
     944                 :          0 :         sub = xs_directory(backend->xs, XBT_NULL, backend->path, &n);
     945                 :          0 :         err = -errno;
     946                 :            :         free(path);
     947                 :            : 
     948                 :          0 :         if (!sub)
     949                 :            :             goto out;
     950                 :            : 
     951                 :            :         /*
     952                 :            :          * Probe each device.
     953                 :            :          */
     954                 :          0 :         for (i = 0; i < n; i++) {
     955                 :          0 :             err = tapback_backend_probe_device(backend, domid, sub[i], NULL);
     956                 :          0 :             if (unlikely(err))
     957                 :            :                 /*
     958                 :            :                  * Keep probing other devices.
     959                 :            :                  */
     960                 :          0 :                 WARN(NULL, "%s error probing device: %s\n",
     961                 :            :                         sub[i], strerror(-err));
     962                 :            :         }
     963                 :            :     }
     964                 :            : 
     965                 :            : out:
     966                 :          0 :         free(sub);
     967                 :          0 :         return err;
     968                 :            : }
     969                 :            : 
     970                 :            : /**
     971                 :            :  * Compares the devices between XenStore and the device list, and
     972                 :            :  * creates/destroys devices accordingly.
     973                 :            :  */
     974                 :            : static int
     975                 :          0 : tapback_probe_domain(backend_t *backend, const domid_t domid)
     976                 :            : {
     977                 :          0 :     vbd_t *device = NULL, *next = NULL;
     978                 :            :     int err;
     979                 :            : 
     980                 :          0 :     ASSERT(backend);
     981                 :            : 
     982                 :            :     /*
     983                 :            :      * scrap all non-existent devices
     984                 :            :      */
     985                 :          0 :     tapback_backend_for_each_device(backend, device, next) {
     986                 :          0 :         if (device->domid == domid) {
     987                 :          0 :             err = tapback_backend_probe_device(backend, device->domid,
     988                 :          0 :                     device->name, NULL);
     989                 :          0 :             if (unlikely(err))
     990                 :            :                 /*
     991                 :            :                  * Keep probing other devices.
     992                 :            :                  */
     993                 :          0 :                 WARN(device, "error probing device: %s\n", strerror(-err));
     994                 :            :         }
     995                 :            :     }
     996                 :            : 
     997                 :          0 :     err = tapback_domain_scan(backend, domid);
     998                 :          0 :     if (err == -ENOENT)
     999                 :          0 :         err = 0;
    1000                 :          0 :     return err;
    1001                 :            : }
    1002                 :            : 
    1003                 :            : /**
    1004                 :            :  * Scans XenStore for all blktap3 devices and probes each one of them.
    1005                 :            :  *
    1006                 :            :  * @returns 0 on success, a negative error code otherwise
    1007                 :            :  *
    1008                 :            :  * XXX Only called by tapback_backend_handle_backend_watch.
    1009                 :            :  */
    1010                 :            : static int
    1011                 :          0 : tapback_backend_scan(backend_t *backend)
    1012                 :            : {
    1013                 :          0 :     vbd_t *device = NULL, *next = NULL;
    1014                 :          0 :     unsigned int i = 0, n = 0;
    1015                 :          0 :     char **dir = NULL;
    1016                 :          0 :     int err = 0;
    1017                 :            : 
    1018                 :          0 :         ASSERT(backend);
    1019                 :            : 
    1020                 :          0 :     DBG(NULL, "scanning entire back-end\n");
    1021                 :            : 
    1022                 :          0 :     if (!tapback_is_master(backend)) {
    1023                 :            :         /*
    1024                 :            :          * scrap all non-existent devices
    1025                 :            :          * TODO Why do we do this? Is this costly?
    1026                 :            :          */
    1027                 :          0 :         tapback_backend_for_each_device(backend, device, next) {
    1028                 :            :             /*
    1029                 :            :              * FIXME check that there is no compoment.
    1030                 :            :              */
    1031                 :          0 :             err = tapback_backend_probe_device(backend, device->domid,
    1032                 :          0 :                     device->name, NULL);
    1033                 :          0 :             if (unlikely(err))
    1034                 :            :                 /*
    1035                 :            :                  * Keep probing other devices.
    1036                 :            :                  */
    1037                 :          0 :                 WARN(device, "error probing device: %s\n", strerror(-err));
    1038                 :            :         }
    1039                 :            :     }
    1040                 :            : 
    1041                 :            :     /*
    1042                 :            :      * probe the new ones
    1043                 :            :      *
    1044                 :            :      * TODO We're checking each and every device in each and every domain,
    1045                 :            :      * could there be a performance issue in the presence of many VMs/VBDs?
    1046                 :            :      * (e.g. boot-storm)
    1047                 :            :      */
    1048                 :          0 :     if (!(dir = xs_directory(backend->xs, XBT_NULL,
    1049                 :          0 :                     backend->path, &n))) {
    1050                 :          0 :         err = -errno;
    1051                 :          0 :         if (err == -ENOENT)
    1052                 :            :             err = 0;
    1053                 :            :         else
    1054                 :          0 :             WARN(NULL, "error listing %s: %s\n", backend->path,
    1055                 :            :                     strerror(err));
    1056                 :            :         goto out;
    1057                 :            :     }
    1058                 :            : 
    1059                 :          0 :     DBG(NULL, "probing %d domains\n", n);
    1060                 :            : 
    1061                 :          0 :     for (i = 0; i < n; i++) { /* for each domain */
    1062                 :          0 :         char *end = NULL;
    1063                 :          0 :         domid_t domid = 0;
    1064                 :            : 
    1065                 :            :         /*
    1066                 :            :          * Get the domain ID.
    1067                 :            :          */
    1068                 :          0 :         domid = strtoul(dir[i], &end, 0);
    1069                 :          0 :         if (*end != 0 || end == dir[i])
    1070                 :          0 :             continue;
    1071                 :            : 
    1072                 :          0 :                 err = tapback_domain_scan(backend, domid);
    1073                 :          0 :                 if (err)
    1074                 :          0 :                         WARN(NULL, "error scanning domain %d: %s\n", domid,
    1075                 :            :                                         strerror(-err));
    1076                 :            :     }
    1077                 :            : 
    1078                 :            : out:
    1079                 :          0 :     free(dir);
    1080                 :          0 :     return err;
    1081                 :            : }
    1082                 :            : 
    1083                 :            : int
    1084                 :          0 : tapback_backend_handle_backend_watch(backend_t *backend,
    1085                 :            :                 char * const path)
    1086                 :            : {
    1087                 :          0 :     char *s = NULL, *end = NULL, *_path = NULL;
    1088                 :          0 :     domid_t domid = 0;
    1089                 :          0 :     int err = 0;
    1090                 :          0 :     bool exists = false;
    1091                 :          0 :     int n = 0;
    1092                 :            : 
    1093                 :          0 :         ASSERT(backend);
    1094                 :          0 :     ASSERT(path);
    1095                 :            : 
    1096                 :          0 :     _path = strdup(path);
    1097                 :          0 :     if (!_path) {
    1098                 :            :         err = -ENOMEM;
    1099                 :            :         goto out;
    1100                 :            :     }
    1101                 :            : 
    1102                 :            :     /*
    1103                 :            :      * path is something like "backend/vbd/domid/devid"
    1104                 :            :      */
    1105                 :            : 
    1106                 :          0 :     s = strtok(_path, "/");
    1107                 :          0 :         if (unlikely(!s)) {
    1108                 :          0 :                 WARN(NULL, "invalid path %s\n", _path);
    1109                 :            :                 err = -EINVAL;
    1110                 :            :                 goto out;
    1111                 :            :         }
    1112                 :          0 :     ASSERT(!strcmp(s, XENSTORE_BACKEND));
    1113                 :          0 :     if (!(s = strtok(NULL, "/"))) {
    1114                 :          0 :         err = tapback_backend_scan(backend);
    1115                 :            :         goto out;
    1116                 :            :     }
    1117                 :            : 
    1118                 :          0 :     ASSERT(!strcmp(s, backend->name));
    1119                 :          0 :     if (!(s = strtok(NULL, "/"))) {
    1120                 :          0 :         err = tapback_backend_scan(backend);
    1121                 :            :         goto out;
    1122                 :            :     }
    1123                 :            : 
    1124                 :          0 :     domid = strtoul(s, &end, 0);
    1125                 :          0 :     if (*end != 0 || end == s) {
    1126                 :          0 :         WARN(NULL, "invalid domain ID \'%s\'\n", s);
    1127                 :            :         err = -EINVAL;
    1128                 :            :         goto out;
    1129                 :            :     }
    1130                 :            : 
    1131                 :            :     /*
    1132                 :            :      * The backend/vbd3/<domain ID> path was either created or removed.
    1133                 :            :      */
    1134                 :          0 :     n = s - _path + strlen(s);
    1135                 :          0 :     err = tapback_xs_exists(backend->xs, XBT_NULL, path, &n);
    1136                 :          0 :     if (err < 0) {
    1137                 :          0 :         WARN(NULL, "failed to read XenStore key %s: %s\n",
    1138                 :            :                 &path[(s - _path)], strerror(-err));
    1139                 :            :         goto out;
    1140                 :            :     }
    1141                 :          0 :     if (err == 0)
    1142                 :            :         exists = false;
    1143                 :            :     else
    1144                 :          0 :         exists = true;
    1145                 :          0 :     err = 0;
    1146                 :            : 
    1147                 :            :     /*
    1148                 :            :      * Master tapback: check if there's tapback for this domain. If there isn't
    1149                 :            :      * one, create it, otherwise ignore this event, the per-domain tapback will
    1150                 :            :      * take care of it.
    1151                 :            :      */
    1152                 :          0 :     if (tapback_is_master(backend)) {
    1153                 :          0 :         struct backend_slave *slave = tapback_find_slave(backend, domid),
    1154                 :          0 :                              **_slave = NULL;
    1155                 :            : 
    1156                 :          0 :         if (!exists && slave) {
    1157                 :          0 :             DBG(NULL, "de-register slave[%d]\n", slave->master.pid);
    1158                 :            :             /*
    1159                 :            :              * remove the slave
    1160                 :            :              */
    1161                 :          0 :             tdelete(slave, &backend->master.slaves, compare);
    1162                 :          0 :             free(slave);
    1163                 :            :         }
    1164                 :          0 :         else if (exists && !slave) {
    1165                 :            :             pid_t pid;
    1166                 :            : 
    1167                 :          0 :             DBG(NULL, "need to create slave for domain %d\n", domid);
    1168                 :            : 
    1169                 :          0 :             pid = fork();
    1170                 :          0 :             if (pid == -1) {
    1171                 :          0 :                 err = -errno;
    1172                 :          0 :                 WARN(NULL, "failed to fork: %s\n", strerror(-err));
    1173                 :            :                 goto out;
    1174                 :          0 :             } else if (pid != 0) { /* parent */
    1175                 :          0 :                 slave = malloc(sizeof(*slave));
    1176                 :          0 :                 if (!slave) {
    1177                 :            :                     int err2;
    1178                 :          0 :                     WARN(NULL, "failed to allocate memory\n");
    1179                 :          0 :                     err = -ENOMEM;
    1180                 :          0 :                     err2 = kill(pid, SIGKILL);
    1181                 :          0 :                     if (err2 != 0) {
    1182                 :          0 :                         err2 = errno;
    1183                 :          0 :                         WARN(NULL, "failed to kill process %d: %s "
    1184                 :            :                                 "(error ignored)\n", pid, strerror(err2));
    1185                 :            :                     }
    1186                 :            :                     goto out;
    1187                 :            :                 }
    1188                 :          0 :                 slave->master.pid = pid;
    1189                 :          0 :                 slave->master.domid = domid;
    1190                 :          0 :                 _slave = tsearch(slave, &backend->master.slaves, compare);
    1191                 :          0 :                 ASSERT(slave == *_slave);
    1192                 :            : 
    1193                 :          0 :                 DBG(NULL, "created slave[%d] for domain %d\n",
    1194                 :            :                         slave->master.pid, slave->master.domid);
    1195                 :            : 
    1196                 :            :                 /*
    1197                 :            :                  * FIXME Shall we watch the child process?
    1198                 :            :                  */
    1199                 :            :             } else { /* child */
    1200                 :            :                 char *args[7];
    1201                 :          0 :                 int i = 0;
    1202                 :            : 
    1203                 :          0 :                 args[i++] = (char*)tapback_name;
    1204                 :          0 :                 args[i++] = "-d";
    1205                 :          0 :                 args[i++] = "-x";
    1206                 :          0 :                 err = asprintf(&args[i++], "%d", domid);
    1207                 :          0 :                 if (err == -1) {
    1208                 :          0 :                     err = -errno;
    1209                 :          0 :                     WARN(NULL, "failed to asprintf: %s\n", strerror(-err));
    1210                 :          0 :                     abort();
    1211                 :            :                 }
    1212                 :          0 :                 if (log_level == LOG_DEBUG)
    1213                 :          0 :                     args[i++] = "-v";
    1214                 :          0 :                                 if (!backend->barrier)
    1215                 :          0 :                                         args[i++] = "-b";
    1216                 :          0 :                 args[i] = NULL;
    1217                 :            :                 /*
    1218                 :            :                  * TODO we're hard-coding the name of the binary, better let
    1219                 :            :                  * the build system supply it.
    1220                 :            :                  */
    1221                 :          0 :                 execvp(tapback_name, args);
    1222                 :          0 :                 err = -errno;
    1223                 :          0 :                 WARN(NULL, "failed to replace master process with slave: %s\n",
    1224                 :            :                         strerror(-err));
    1225                 :          0 :                 abort();
    1226                 :            :             }
    1227                 :            :         }
    1228                 :            :         err = 0;
    1229                 :            :     } else {
    1230                 :          0 :         char *device = NULL;
    1231                 :            : 
    1232                 :          0 :         ASSERT(domid == backend->slave_domid);
    1233                 :            : 
    1234                 :          0 :         if (!exists) {
    1235                 :            :             /*
    1236                 :            :              * The entire domain may be removed in one go, so we need to tear
    1237                 :            :              * down all devices.
    1238                 :            :              */
    1239                 :          0 :             err = tapback_probe_domain(backend, domid);
    1240                 :          0 :             if (err)
    1241                 :          0 :                 WARN(NULL, "failed to probe domain: %s\n", strerror(-err));
    1242                 :            : 
    1243                 :            :             /*
    1244                 :            :              * Time to go.
    1245                 :            :              */
    1246                 :          0 :             INFO(NULL, "domain removed, exit\n");
    1247                 :          0 :             tapback_backend_destroy(backend);
    1248                 :          0 :             exit(EXIT_SUCCESS);
    1249                 :            : 
    1250                 :            :             /*
    1251                 :            :              * R.I.P.
    1252                 :            :              */
    1253                 :            :         }
    1254                 :            : 
    1255                 :            :         /*
    1256                 :            :          * There's no device yet, the domain just got created, nothing to do
    1257                 :            :          * just yet. However, the entire sub-tree might have gotten created
    1258                 :            :          * before the slave so we still need to check whether there are any
    1259                 :            :          * devices.
    1260                 :            :          */
    1261                 :          0 :         device = strtok(NULL, "/");
    1262                 :          0 :         if (!device) {
    1263                 :          0 :             err = tapback_probe_domain(backend, domid);
    1264                 :            :             goto out;
    1265                 :            :         }
    1266                 :            : 
    1267                 :            :         /*
    1268                 :            :          * Create or remove a specific device.
    1269                 :            :          *
    1270                 :            :          * TODO tapback_backend_probe_device reads xenstore again to see if the
    1271                 :            :          * device should exist, but we already know that in the current
    1272                 :            :          * function.
    1273                 :            :          *
    1274                 :            :          * Optimise this case.
    1275                 :            :          */
    1276                 :          0 :         err = tapback_backend_probe_device(backend, domid, device,
    1277                 :          0 :                 strtok(NULL, "/"));
    1278                 :            :     }
    1279                 :            : out:
    1280                 :          0 :     free(_path);
    1281                 :          0 :     return err;
    1282                 :            : }
    1283                 :            : 
    1284                 :            : bool
    1285                 :          0 : verbose(void)
    1286                 :            : {
    1287                 :          0 :     return log_level >= LOG_DEBUG;
    1288                 :            : }

Generated by: LCOV version 1.13