Merge tag 'xarray-5.1-rc1' of git://git.infradead.org/users/willy/linux-dax
authorLinus Torvalds <torvalds@linux-foundation.org>
Tue, 12 Mar 2019 03:06:18 +0000 (20:06 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 12 Mar 2019 03:06:18 +0000 (20:06 -0700)
Pull XArray updates from Matthew Wilcox:
 "This pull request changes the xa_alloc() API. I'm only aware of one
  subsystem that has started trying to use it, and we agree on the fixup
  as part of the merge.

  The xa_insert() error code also changed to match xa_alloc() (EEXIST to
  EBUSY), and I added xa_alloc_cyclic(). Beyond that, the usual
  bugfixes, optimisations and tweaking.

  I now have a git tree with all users of the radix tree and IDR
  converted over to the XArray that I'll be feeding to maintainers over
  the next few weeks"

* tag 'xarray-5.1-rc1' of git://git.infradead.org/users/willy/linux-dax:
  XArray: Fix xa_reserve for 2-byte aligned entries
  XArray: Fix xa_erase of 2-byte aligned entries
  XArray: Use xa_cmpxchg to implement xa_reserve
  XArray: Fix xa_release in allocating arrays
  XArray: Mark xa_insert and xa_reserve as must_check
  XArray: Add cyclic allocation
  XArray: Redesign xa_alloc API
  XArray: Add support for 1s-based allocation
  XArray: Change xa_insert to return -EBUSY
  XArray: Update xa_erase family descriptions
  XArray tests: RCU lock prohibits GFP_KERNEL

1  2 
drivers/infiniband/core/device.c
drivers/infiniband/core/restrack.c

@@@ -644,47 -459,28 +644,38 @@@ static int ib_security_change(struct no
        return NOTIFY_OK;
  }
  
 -/**
 - *    __dev_new_index -       allocate an device index
 - *
 - *    Returns a suitable unique value for a new device interface
 - *    number.  It assumes that there are less than 2^32-1 ib devices
 - *    will be present in the system.
 +/*
 + * Assign the unique string device name and the unique device index. This is
 + * undone by ib_dealloc_device.
   */
 -static u32 __dev_new_index(void)
 +static int assign_name(struct ib_device *device, const char *name)
  {
 -      /*
 -       * The device index to allow stable naming.
 -       * Similar to struct net -> ifindex.
 -       */
 -      static u32 index;
 +      static u32 last_id;
 +      int ret;
  
 -      for (;;) {
 -              if (!(++index))
 -                      index = 1;
 +      down_write(&devices_rwsem);
 +      /* Assign a unique name to the device */
 +      if (strchr(name, '%'))
 +              ret = alloc_name(device, name);
 +      else
 +              ret = dev_set_name(&device->dev, name);
 +      if (ret)
 +              goto out;
  
 -              if (!__ib_device_get_by_index(index))
 -                      return index;
 +      if (__ib_device_get_by_name(dev_name(&device->dev))) {
 +              ret = -ENFILE;
 +              goto out;
        }
-       /* Cyclically allocate a user visible ID for the device */
-       device->index = last_id;
-       ret = xa_alloc(&devices, &device->index, INT_MAX, device, GFP_KERNEL);
-       if (ret == -ENOSPC) {
-               device->index = 0;
-               ret = xa_alloc(&devices, &device->index, INT_MAX, device,
-                              GFP_KERNEL);
-       }
-       if (ret)
-               goto out;
-       last_id = device->index + 1;
-       ret = 0;
 +      strlcpy(device->name, dev_name(&device->dev), IB_DEVICE_NAME_MAX);
 +
++      ret = xa_alloc_cyclic(&devices, &device->index, device, xa_limit_31b,
++                      &last_id, GFP_KERNEL);
++      if (ret > 0)
++              ret = 0;
 +
 +out:
 +      up_write(&devices_rwsem);
 +      return ret;
  }
  
  static void setup_dma_device(struct ib_device *device)
@@@ -902,181 -649,63 +893,182 @@@ cg_cleanup
  }
  EXPORT_SYMBOL(ib_register_device);
  
 +/* Callers must hold a get on the device. */
 +static void __ib_unregister_device(struct ib_device *ib_dev)
 +{
 +      /*
 +       * We have a registration lock so that all the calls to unregister are
 +       * fully fenced, once any unregister returns the device is truely
 +       * unregistered even if multiple callers are unregistering it at the
 +       * same time. This also interacts with the registration flow and
 +       * provides sane semantics if register and unregister are racing.
 +       */
 +      mutex_lock(&ib_dev->unregistration_lock);
 +      if (!refcount_read(&ib_dev->refcount))
 +              goto out;
 +
 +      disable_device(ib_dev);
 +      ib_device_unregister_sysfs(ib_dev);
 +      device_del(&ib_dev->dev);
 +      ib_device_unregister_rdmacg(ib_dev);
 +      ib_cache_cleanup_one(ib_dev);
 +
 +      /*
 +       * Drivers using the new flow may not call ib_dealloc_device except
 +       * in error unwind prior to registration success.
 +       */
 +      if (ib_dev->ops.dealloc_driver) {
 +              WARN_ON(kref_read(&ib_dev->dev.kobj.kref) <= 1);
 +              ib_dealloc_device(ib_dev);
 +      }
 +out:
 +      mutex_unlock(&ib_dev->unregistration_lock);
 +}
 +
  /**
   * ib_unregister_device - Unregister an IB device
 - * @device:Device to unregister
 + * @device: The device to unregister
   *
   * Unregister an IB device.  All clients will receive a remove callback.
 + *
 + * Callers should call this routine only once, and protect against races with
 + * registration. Typically it should only be called as part of a remove
 + * callback in an implementation of driver core's struct device_driver and
 + * related.
 + *
 + * If ops.dealloc_driver is used then ib_dev will be freed upon return from
 + * this function.
   */
 -void ib_unregister_device(struct ib_device *device)
 +void ib_unregister_device(struct ib_device *ib_dev)
  {
 -      struct ib_client_data *context, *tmp;
 -      unsigned long flags;
 +      get_device(&ib_dev->dev);
 +      __ib_unregister_device(ib_dev);
 +      put_device(&ib_dev->dev);
 +}
 +EXPORT_SYMBOL(ib_unregister_device);
  
 -      /*
 -       * Wait for all netlink command callers to finish working on the
 -       * device.
 -       */
 -      ib_device_put(device);
 -      wait_for_completion(&device->unreg_completion);
 +/**
 + * ib_unregister_device_and_put - Unregister a device while holding a 'get'
 + * device: The device to unregister
 + *
 + * This is the same as ib_unregister_device(), except it includes an internal
 + * ib_device_put() that should match a 'get' obtained by the caller.
 + *
 + * It is safe to call this routine concurrently from multiple threads while
 + * holding the 'get'. When the function returns the device is fully
 + * unregistered.
 + *
 + * Drivers using this flow MUST use the driver_unregister callback to clean up
 + * their resources associated with the device and dealloc it.
 + */
 +void ib_unregister_device_and_put(struct ib_device *ib_dev)
 +{
 +      WARN_ON(!ib_dev->ops.dealloc_driver);
 +      get_device(&ib_dev->dev);
 +      ib_device_put(ib_dev);
 +      __ib_unregister_device(ib_dev);
 +      put_device(&ib_dev->dev);
 +}
 +EXPORT_SYMBOL(ib_unregister_device_and_put);
  
 -      mutex_lock(&device_mutex);
 +/**
 + * ib_unregister_driver - Unregister all IB devices for a driver
 + * @driver_id: The driver to unregister
 + *
 + * This implements a fence for device unregistration. It only returns once all
 + * devices associated with the driver_id have fully completed their
 + * unregistration and returned from ib_unregister_device*().
 + *
 + * If device's are not yet unregistered it goes ahead and starts unregistering
 + * them.
 + *
 + * This does not block creation of new devices with the given driver_id, that
 + * is the responsibility of the caller.
 + */
 +void ib_unregister_driver(enum rdma_driver_id driver_id)
 +{
 +      struct ib_device *ib_dev;
 +      unsigned long index;
 +
 +      down_read(&devices_rwsem);
 +      xa_for_each (&devices, index, ib_dev) {
 +              if (ib_dev->driver_id != driver_id)
 +                      continue;
 +
 +              get_device(&ib_dev->dev);
 +              up_read(&devices_rwsem);
  
 -      down_write(&lists_rwsem);
 -      list_del(&device->core_list);
 -      write_lock_irq(&device->client_data_lock);
 -      list_for_each_entry(context, &device->client_data_list, list)
 -              context->going_down = true;
 -      write_unlock_irq(&device->client_data_lock);
 -      downgrade_write(&lists_rwsem);
 +              WARN_ON(!ib_dev->ops.dealloc_driver);
 +              __ib_unregister_device(ib_dev);
  
 -      list_for_each_entry(context, &device->client_data_list, list) {
 -              if (context->client->remove)
 -                      context->client->remove(device, context->data);
 +              put_device(&ib_dev->dev);
 +              down_read(&devices_rwsem);
        }
 -      up_read(&lists_rwsem);
 +      up_read(&devices_rwsem);
 +}
 +EXPORT_SYMBOL(ib_unregister_driver);
  
 -      ib_device_unregister_sysfs(device);
 -      ib_device_unregister_rdmacg(device);
 +static void ib_unregister_work(struct work_struct *work)
 +{
 +      struct ib_device *ib_dev =
 +              container_of(work, struct ib_device, unregistration_work);
  
 -      mutex_unlock(&device_mutex);
 +      __ib_unregister_device(ib_dev);
 +      put_device(&ib_dev->dev);
 +}
  
 -      ib_cache_cleanup_one(device);
 +/**
 + * ib_unregister_device_queued - Unregister a device using a work queue
 + * device: The device to unregister
 + *
 + * This schedules an asynchronous unregistration using a WQ for the device. A
 + * driver should use this to avoid holding locks while doing unregistration,
 + * such as holding the RTNL lock.
 + *
 + * Drivers using this API must use ib_unregister_driver before module unload
 + * to ensure that all scheduled unregistrations have completed.
 + */
 +void ib_unregister_device_queued(struct ib_device *ib_dev)
 +{
 +      WARN_ON(!refcount_read(&ib_dev->refcount));
 +      WARN_ON(!ib_dev->ops.dealloc_driver);
 +      get_device(&ib_dev->dev);
 +      if (!queue_work(system_unbound_wq, &ib_dev->unregistration_work))
 +              put_device(&ib_dev->dev);
 +}
 +EXPORT_SYMBOL(ib_unregister_device_queued);
  
 -      ib_security_destroy_port_pkey_list(device);
 -      kfree(device->port_pkey_list);
 +static int assign_client_id(struct ib_client *client)
 +{
 +      int ret;
  
 -      down_write(&lists_rwsem);
 -      write_lock_irqsave(&device->client_data_lock, flags);
 -      list_for_each_entry_safe(context, tmp, &device->client_data_list,
 -                               list) {
 -              list_del(&context->list);
 -              kfree(context);
 +      down_write(&clients_rwsem);
 +      /*
 +       * The add/remove callbacks must be called in FIFO/LIFO order. To
 +       * achieve this we assign client_ids so they are sorted in
 +       * registration order, and retain a linked list we can reverse iterate
 +       * to get the LIFO order. The extra linked list can go away if xarray
 +       * learns to reverse iterate.
 +       */
-       if (list_empty(&client_list))
++      if (list_empty(&client_list)) {
 +              client->client_id = 0;
-       else
-               client->client_id =
-                       list_last_entry(&client_list, struct ib_client, list)
-                               ->client_id;
-       ret = xa_alloc(&clients, &client->client_id, INT_MAX, client,
-                      GFP_KERNEL);
++      } else {
++              struct ib_client *last;
++
++              last = list_last_entry(&client_list, struct ib_client, list);
++              client->client_id = last->client_id + 1;
+       }
 -      write_unlock_irqrestore(&device->client_data_lock, flags);
 -      up_write(&lists_rwsem);
++      ret = xa_insert(&clients, client->client_id, client, GFP_KERNEL);
 +      if (ret)
 +              goto out;
  
 -      device->reg_state = IB_DEV_UNREGISTERED;
 +      xa_set_mark(&clients, client->client_id, CLIENT_REGISTERED);
 +      list_add_tail(&client->list, &client_list);
 +
 +out:
 +      up_write(&clients_rwsem);
 +      return ret;
  }
 -EXPORT_SYMBOL(ib_unregister_device);
  
  /**
   * ib_register_client - Register an IB client
  #include <linux/pid_namespace.h>
  
  #include "cma_priv.h"
 +#include "restrack.h"
  
- static int rt_xa_alloc_cyclic(struct xarray *xa, u32 *id, void *entry,
-                             u32 *next)
- {
-       int err;
-       *id = *next;
-       if (*next == U32_MAX)
-               *id = 0;
-       xa_lock(xa);
-       err = __xa_alloc(xa, id, U32_MAX, entry, GFP_KERNEL);
-       if (err && *next != U32_MAX) {
-               *id = 0;
-               err = __xa_alloc(xa, id, *next, entry, GFP_KERNEL);
-       }
-       if (!err)
-               *next = *id + 1;
-       xa_unlock(xa);
-       return err;
- }
 -static int fill_res_noop(struct sk_buff *msg,
 -                       struct rdma_restrack_entry *entry)
 +/**
 + * rdma_restrack_init() - initialize and allocate resource tracking
 + * @dev:  IB device
 + *
 + * Return: 0 on success
 + */
 +int rdma_restrack_init(struct ib_device *dev)
  {
 -      return 0;
 -}
 +      struct rdma_restrack_root *rt;
 +      int i;
  
 -void rdma_restrack_init(struct rdma_restrack_root *res)
 -{
 -      init_rwsem(&res->rwsem);
 -      res->fill_res_entry = fill_res_noop;
 +      dev->res = kcalloc(RDMA_RESTRACK_MAX, sizeof(*rt), GFP_KERNEL);
 +      if (!dev->res)
 +              return -ENOMEM;
 +
 +      rt = dev->res;
 +
 +      for (i = 0; i < RDMA_RESTRACK_MAX; i++)
 +              xa_init_flags(&rt[i].xa, XA_FLAGS_ALLOC);
 +
 +      return 0;
  }
  
  static const char *type2str(enum rdma_restrack_type type)
@@@ -225,18 -174,11 +203,19 @@@ static void rdma_restrack_add(struct rd
  
        kref_init(&res->kref);
        init_completion(&res->comp);
 -      res->valid = true;
 +      if (res->type != RDMA_RESTRACK_QP)
-               ret = rt_xa_alloc_cyclic(&rt->xa, &res->id, res, &rt->next_id);
++              ret = xa_alloc_cyclic(&rt->xa, &res->id, res, xa_limit_32b,
++                              &rt->next_id, GFP_KERNEL);
 +      else {
 +              /* Special case to ensure that LQPN points to right QP */
 +              struct ib_qp *qp = container_of(res, struct ib_qp, res);
 +
 +              ret = xa_insert(&rt->xa, qp->qp_num, res, GFP_KERNEL);
 +              res->id = ret ? 0 : qp->qp_num;
 +      }
  
 -      down_write(&dev->res.rwsem);
 -      hash_add(dev->res.hash, &res->node, res->type);
 -      up_write(&dev->res.rwsem);
 +      if (!ret)
 +              res->valid = true;
  }
  
  /**