Commit 6d400d7c authored by Fabio M. De Francesco's avatar Fabio M. De Francesco Committed by Greg Kroah-Hartman
Browse files

staging: unisys: visorhba: Convert module from IDR to XArray



Converted visorhba from IDR to XArray. The abstract data type XArray is
more memory-efficient, parallelizable, and cache friendly. It takes
advantage of RCU to perform lookups without locking. Furthermore, IDR is
deprecated because XArray has a better (cleaner and more consistent)
API.

Reviewed-by: default avatarDan Carpenter <dan.carpenter@oracle.com>
Signed-off-by: default avatarFabio M. De Francesco <fmdefrancesco@gmail.com>
Link: https://lore.kernel.org/r/20210514081112.19542-1-fmdefrancesco@gmail.com


Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent e02a3b94
Loading
Loading
Loading
Loading
+41 −60
Original line number Diff line number Diff line
@@ -6,10 +6,10 @@

#include <linux/debugfs.h>
#include <linux/kthread.h>
#include <linux/idr.h>
#include <linux/module.h>
#include <linux/seq_file.h>
#include <linux/visorbus.h>
#include <linux/xarray.h>
#include <scsi/scsi.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_cmnd.h>
@@ -82,8 +82,7 @@ struct visorhba_devdata {
	 * allows us to pass int handles back-and-forth between us and
	 * iovm, instead of raw pointers
	 */
	struct idr idr;

	struct xarray xa;
	struct dentry *debugfs_dir;
	struct dentry *debugfs_info;
};
@@ -182,71 +181,48 @@ static struct uiscmdrsp *get_scsipending_cmdrsp(struct visorhba_devdata *ddata,
	return NULL;
}

/*
 * simple_idr_get - Associate a provided pointer with an int value
 *		    1 <= value <= INT_MAX, and return this int value;
 *		    the pointer value can be obtained later by passing
 *		    this int value to idr_find()
 * @idrtable: The data object maintaining the pointer<-->int mappings
 * @p:	      The pointer value to be remembered
 * @lock:     A spinlock used when exclusive access to idrtable is needed
 *
 * Return: The id number mapped to pointer 'p', 0 on failure
 */
static unsigned int simple_idr_get(struct idr *idrtable, void *p,
				   spinlock_t *lock)
{
	int id;
	unsigned long flags;

	idr_preload(GFP_KERNEL);
	spin_lock_irqsave(lock, flags);
	id = idr_alloc(idrtable, p, 1, INT_MAX, GFP_NOWAIT);
	spin_unlock_irqrestore(lock, flags);
	idr_preload_end();
	/* failure */
	if (id < 0)
		return 0;
	/* idr_alloc() guarantees > 0 */
	return (unsigned int)(id);
}

/*
 * setup_scsitaskmgmt_handles - Stash the necessary handles so that the
 *				completion processing logic for a taskmgmt
 *				cmd will be able to find who to wake up
 *				and where to stash the result
 * @idrtable: The data object maintaining the pointer<-->int mappings
 * @lock:     A spinlock used when exclusive access to idrtable is needed
 * @xa:       The data object maintaining the pointer<-->int mappings
 * @cmdrsp:   Response from the IOVM
 * @event:    The event handle to associate with an id
 * @result:   The location to place the result of the event handle into
 */
static void setup_scsitaskmgmt_handles(struct idr *idrtable, spinlock_t *lock,
				       struct uiscmdrsp *cmdrsp,
static int setup_scsitaskmgmt_handles(struct xarray *xa, struct uiscmdrsp *cmdrsp,
				       wait_queue_head_t *event, int *result)
{
	/* specify the event that has to be triggered when this */
	/* cmd is complete */
	cmdrsp->scsitaskmgmt.notify_handle =
		simple_idr_get(idrtable, event, lock);
	cmdrsp->scsitaskmgmt.notifyresult_handle =
		simple_idr_get(idrtable, result, lock);
	int ret;
	u32 id;

	/* specify the event that has to be triggered when this cmd is complete */
	ret = xa_alloc_irq(xa, &id, event, xa_limit_32b, GFP_KERNEL);
	if (ret)
		return ret;
	cmdrsp->scsitaskmgmt.notify_handle = id;
	ret = xa_alloc_irq(xa, &id, result, xa_limit_32b, GFP_KERNEL);
	if (ret) {
		xa_erase_irq(xa, cmdrsp->scsitaskmgmt.notify_handle);
		return ret;
	}
	cmdrsp->scsitaskmgmt.notifyresult_handle = id;

	return 0;
}

/*
 * cleanup_scsitaskmgmt_handles - Forget handles created by
 *				  setup_scsitaskmgmt_handles()
 * @idrtable: The data object maintaining the pointer<-->int mappings
 * @xa: The data object maintaining the pointer<-->int mappings
 * @cmdrsp:   Response from the IOVM
 */
static void cleanup_scsitaskmgmt_handles(struct idr *idrtable,
static void cleanup_scsitaskmgmt_handles(struct xarray *xa,
					 struct uiscmdrsp *cmdrsp)
{
	if (cmdrsp->scsitaskmgmt.notify_handle)
		idr_remove(idrtable, cmdrsp->scsitaskmgmt.notify_handle);
	if (cmdrsp->scsitaskmgmt.notifyresult_handle)
		idr_remove(idrtable, cmdrsp->scsitaskmgmt.notifyresult_handle);
	xa_erase_irq(xa, cmdrsp->scsitaskmgmt.notify_handle);
	xa_erase_irq(xa, cmdrsp->scsitaskmgmt.notifyresult_handle);
}

/*
@@ -269,6 +245,7 @@ static int forward_taskmgmt_command(enum task_mgmt_types tasktype,
	int notifyresult = 0xffff;
	wait_queue_head_t notifyevent;
	int scsicmd_id;
	int ret;

	if (devdata->serverdown || devdata->serverchangingstate)
		return FAILED;
@@ -284,8 +261,14 @@ static int forward_taskmgmt_command(enum task_mgmt_types tasktype,

	/* issue TASK_MGMT_ABORT_TASK */
	cmdrsp->cmdtype = CMD_SCSITASKMGMT_TYPE;
	setup_scsitaskmgmt_handles(&devdata->idr, &devdata->privlock, cmdrsp,

	ret = setup_scsitaskmgmt_handles(&devdata->xa, cmdrsp,
					 &notifyevent, &notifyresult);
	if (ret) {
		dev_dbg(&scsidev->sdev_gendev,
		        "visorhba: setup_scsitaskmgmt_handles returned %d\n", ret);
		return FAILED;
	}

	/* save destination */
	cmdrsp->scsitaskmgmt.tasktype = tasktype;
@@ -311,14 +294,14 @@ static int forward_taskmgmt_command(enum task_mgmt_types tasktype,
	dev_dbg(&scsidev->sdev_gendev,
		"visorhba: taskmgmt type=%d success; result=0x%x\n",
		 tasktype, notifyresult);
	cleanup_scsitaskmgmt_handles(&devdata->idr, cmdrsp);
	cleanup_scsitaskmgmt_handles(&devdata->xa, cmdrsp);
	return SUCCESS;

err_del_scsipending_ent:
	dev_dbg(&scsidev->sdev_gendev,
		"visorhba: taskmgmt type=%d not executed\n", tasktype);
	del_scsipending_ent(devdata, scsicmd_id);
	cleanup_scsitaskmgmt_handles(&devdata->idr, cmdrsp);
	cleanup_scsitaskmgmt_handles(&devdata->xa, cmdrsp);
	return FAILED;
}

@@ -654,13 +637,13 @@ DEFINE_SHOW_ATTRIBUTE(info_debugfs);
 * Service Partition returned the result of the task management
 * command. Wake up anyone waiting for it.
 */
static void complete_taskmgmt_command(struct idr *idrtable,
static void complete_taskmgmt_command(struct xarray *xa,
				      struct uiscmdrsp *cmdrsp, int result)
{
	wait_queue_head_t *wq =
		idr_find(idrtable, cmdrsp->scsitaskmgmt.notify_handle);
		xa_load(xa, cmdrsp->scsitaskmgmt.notify_handle);
	int *scsi_result_ptr =
		idr_find(idrtable, cmdrsp->scsitaskmgmt.notifyresult_handle);
		xa_load(xa, cmdrsp->scsitaskmgmt.notifyresult_handle);
	if (unlikely(!(wq && scsi_result_ptr))) {
		pr_err("visorhba: no completion context; cmd will time out\n");
		return;
@@ -708,7 +691,7 @@ static void visorhba_serverdown_complete(struct visorhba_devdata *devdata)
			break;
		case CMD_SCSITASKMGMT_TYPE:
			cmdrsp = pendingdel->sent;
			complete_taskmgmt_command(&devdata->idr, cmdrsp,
			complete_taskmgmt_command(&devdata->xa, cmdrsp,
						  TASK_MGMT_FAILED);
			break;
		default:
@@ -905,7 +888,7 @@ static void drain_queue(struct uiscmdrsp *cmdrsp,
			if (!del_scsipending_ent(devdata,
						 cmdrsp->scsitaskmgmt.handle))
				break;
			complete_taskmgmt_command(&devdata->idr, cmdrsp,
			complete_taskmgmt_command(&devdata->xa, cmdrsp,
						  cmdrsp->scsitaskmgmt.result);
		} else if (cmdrsp->cmdtype == CMD_NOTIFYGUEST_TYPE)
			dev_err_once(&devdata->dev->device,
@@ -1053,7 +1036,7 @@ static int visorhba_probe(struct visor_device *dev)
	if (err)
		goto err_debugfs_info;

	idr_init(&devdata->idr);
	xa_init(&devdata->xa);

	devdata->cmdrsp = kmalloc(sizeof(*devdata->cmdrsp), GFP_ATOMIC);
	visorbus_enable_channel_interrupts(dev);
@@ -1096,8 +1079,6 @@ static void visorhba_remove(struct visor_device *dev)
	scsi_remove_host(scsihost);
	scsi_host_put(scsihost);

	idr_destroy(&devdata->idr);

	dev_set_drvdata(&dev->device, NULL);
	debugfs_remove(devdata->debugfs_info);
	debugfs_remove_recursive(devdata->debugfs_dir);