ia64/xen-unstable

changeset 19658:28a197617286

Fix up the synchronisation around grant table map track handles.
At present, we're not doing any at all, so if a domain e.g. tries to
do two map operations at the same time from different vcpus then you
could end up with both operations getting back the same maptrack
handle.

Fix this problem by just shoving an enormous lock around grant table
operations. This is unlikely to be heavily contended, because netback
and blkback both restrict themselves to mapping on a single vcpu at a
time (globally for netback, and per-device for blkback), and most of
the interesting bits are already protected by the remote domain's
grant table lock anyway.

The unconteded acquisition cost might be significant for some
workloads. If that were the case, it might be worth only acquiring
the lock only for multi-vcpu domains, since we only manipulate the
maptrack table in the context of one of the domain's vcpus. I've not
done that optimisation here, because I didn't want to think about what
would happen if e.g. a cpu got hot-unplugged from a domain while it
was performing a map operation.

Signed-off-by: Steven Smith <steven.smith@citrix.com>
author Keir Fraser <keir.fraser@citrix.com>
date Wed May 27 11:29:38 2009 +0100 (2009-05-27)
parents 9ff5c79b0ceb
children 411ecf6d1f19
files xen/common/grant_table.c xen/include/xen/grant_table.h
line diff
     1.1 --- a/xen/common/grant_table.c	Wed May 27 11:28:45 2009 +0100
     1.2 +++ b/xen/common/grant_table.c	Wed May 27 11:29:38 2009 +0100
     1.3 @@ -111,6 +111,26 @@ static unsigned inline int max_nr_maptra
     1.4  #define active_entry(t, e) \
     1.5      ((t)->active[(e)/ACGNT_PER_PAGE][(e)%ACGNT_PER_PAGE])
     1.6  
     1.7 +/* Technically, we only really need to acquire the lock for SMP
     1.8 +   guests, because you only ever touch the maptrack tables from the
     1.9 +   context of the guest which owns them, so if it's uniproc then the
    1.10 +   lock can't be contended, and is therefore pointless.  Don't bother
    1.11 +   with that optimisation for now, though, because it's scary and
    1.12 +   confusing. */
    1.13 +/* The maptrack lock is top-level: you're not allowed to be holding
    1.14 +   any other locks when you acquire it. */
    1.15 +static void
    1.16 +maptrack_lock(struct grant_table *lgt)
    1.17 +{
    1.18 +    spin_lock(&lgt->maptrack_lock);
    1.19 +}
    1.20 +
    1.21 +static void
    1.22 +maptrack_unlock(struct grant_table *lgt)
    1.23 +{
    1.24 +    spin_unlock(&lgt->maptrack_lock);
    1.25 +}
    1.26 +
    1.27  static inline int
    1.28  __get_maptrack_handle(
    1.29      struct grant_table *t)
    1.30 @@ -141,43 +161,30 @@ get_maptrack_handle(
    1.31  
    1.32      if ( unlikely((handle = __get_maptrack_handle(lgt)) == -1) )
    1.33      {
    1.34 -        spin_lock(&lgt->lock);
    1.35 -
    1.36 -        if ( unlikely((handle = __get_maptrack_handle(lgt)) == -1) )
    1.37 -        {
    1.38 -            nr_frames = nr_maptrack_frames(lgt);
    1.39 -            if ( nr_frames >= max_nr_maptrack_frames() )
    1.40 -            {
    1.41 -                spin_unlock(&lgt->lock);
    1.42 -                return -1;
    1.43 -            }
    1.44 +        nr_frames = nr_maptrack_frames(lgt);
    1.45 +        if ( nr_frames >= max_nr_maptrack_frames() )
    1.46 +            return -1;
    1.47  
    1.48 -            new_mt = alloc_xenheap_page();
    1.49 -            if ( new_mt == NULL )
    1.50 -            {
    1.51 -                spin_unlock(&lgt->lock);
    1.52 -                return -1;
    1.53 -            }
    1.54 -
    1.55 -            clear_page(new_mt);
    1.56 -
    1.57 -            new_mt_limit = lgt->maptrack_limit + MAPTRACK_PER_PAGE;
    1.58 +        new_mt = alloc_xenheap_page();
    1.59 +        if ( new_mt == NULL )
    1.60 +            return -1;
    1.61  
    1.62 -            for ( i = lgt->maptrack_limit; i < new_mt_limit; i++ )
    1.63 -            {
    1.64 -                new_mt[i % MAPTRACK_PER_PAGE].ref = i+1;
    1.65 -                new_mt[i % MAPTRACK_PER_PAGE].flags = 0;
    1.66 -            }
    1.67 +        clear_page(new_mt);
    1.68  
    1.69 -            lgt->maptrack[nr_frames] = new_mt;
    1.70 -            lgt->maptrack_limit      = new_mt_limit;
    1.71 +        new_mt_limit = lgt->maptrack_limit + MAPTRACK_PER_PAGE;
    1.72  
    1.73 -            gdprintk(XENLOG_INFO,
    1.74 -                    "Increased maptrack size to %u frames.\n", nr_frames + 1);
    1.75 -            handle = __get_maptrack_handle(lgt);
    1.76 +        for ( i = lgt->maptrack_limit; i < new_mt_limit; i++ )
    1.77 +        {
    1.78 +            new_mt[i % MAPTRACK_PER_PAGE].ref = i+1;
    1.79 +            new_mt[i % MAPTRACK_PER_PAGE].flags = 0;
    1.80          }
    1.81  
    1.82 -        spin_unlock(&lgt->lock);
    1.83 +        lgt->maptrack[nr_frames] = new_mt;
    1.84 +        lgt->maptrack_limit      = new_mt_limit;
    1.85 +
    1.86 +        gdprintk(XENLOG_INFO,
    1.87 +                 "Increased maptrack size to %u frames.\n", nr_frames + 1);
    1.88 +        handle = __get_maptrack_handle(lgt);
    1.89      }
    1.90      return handle;
    1.91  }
    1.92 @@ -1508,7 +1515,9 @@ do_grant_table_op(
    1.93              guest_handle_cast(uop, gnttab_map_grant_ref_t);
    1.94          if ( unlikely(!guest_handle_okay(map, count)) )
    1.95              goto out;
    1.96 +        maptrack_lock(current->domain->grant_table);
    1.97          rc = gnttab_map_grant_ref(map, count);
    1.98 +        maptrack_unlock(current->domain->grant_table);
    1.99          break;
   1.100      }
   1.101      case GNTTABOP_unmap_grant_ref:
   1.102 @@ -1517,7 +1526,9 @@ do_grant_table_op(
   1.103              guest_handle_cast(uop, gnttab_unmap_grant_ref_t);
   1.104          if ( unlikely(!guest_handle_okay(unmap, count)) )
   1.105              goto out;
   1.106 +        maptrack_lock(current->domain->grant_table);
   1.107          rc = gnttab_unmap_grant_ref(unmap, count);
   1.108 +        maptrack_unlock(current->domain->grant_table);
   1.109          break;
   1.110      }
   1.111      case GNTTABOP_unmap_and_replace:
   1.112 @@ -1529,7 +1540,9 @@ do_grant_table_op(
   1.113          rc = -ENOSYS;
   1.114          if ( unlikely(!replace_grant_supported()) )
   1.115              goto out;
   1.116 +        maptrack_lock(current->domain->grant_table);
   1.117          rc = gnttab_unmap_and_replace(unmap, count);
   1.118 +        maptrack_unlock(current->domain->grant_table);
   1.119          break;
   1.120      }
   1.121      case GNTTABOP_setup_table:
   1.122 @@ -1600,6 +1613,7 @@ grant_table_create(
   1.123      /* Simple stuff. */
   1.124      memset(t, 0, sizeof(*t));
   1.125      spin_lock_init(&t->lock);
   1.126 +    spin_lock_init(&t->maptrack_lock);
   1.127      t->nr_grant_frames = INITIAL_NR_GRANT_FRAMES;
   1.128  
   1.129      /* Active grant table. */
   1.130 @@ -1680,6 +1694,7 @@ gnttab_release_mappings(
   1.131  
   1.132      for ( handle = 0; handle < gt->maptrack_limit; handle++ )
   1.133      {
   1.134 +        /* Domain is dying, so don't need maptrack lock */
   1.135          map = &maptrack_entry(gt, handle);
   1.136          if ( !(map->flags & (GNTMAP_device_map|GNTMAP_host_map)) )
   1.137              continue;
     2.1 --- a/xen/include/xen/grant_table.h	Wed May 27 11:28:45 2009 +0100
     2.2 +++ b/xen/include/xen/grant_table.h	Wed May 27 11:29:38 2009 +0100
     2.3 @@ -91,6 +91,8 @@ struct grant_table {
     2.4      struct grant_mapping **maptrack;
     2.5      unsigned int          maptrack_head;
     2.6      unsigned int          maptrack_limit;
     2.7 +    /* Lock protecting maptrack-related fields. */
     2.8 +    spinlock_t            maptrack_lock;
     2.9      /* Lock protecting updates to active and shared grant tables. */
    2.10      spinlock_t            lock;
    2.11  };