1121 lines · 28.8 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-or-later/* *	Adaptec AAC series RAID controller driver *	(c) Copyright 2001 Red Hat Inc. * * based on the old aacraid driver that is.. * Adaptec aacraid device driver for Linux. * * Copyright (c) 2000-2010 Adaptec, Inc. *               2010-2015 PMC-Sierra, Inc. (aacraid@pmc-sierra.com) *		 2016-2017 Microsemi Corp. (aacraid@microsemi.com) * * Module Name: *  commctrl.c * * Abstract: Contains all routines for control of the AFA comm layer */ #include <linux/kernel.h>#include <linux/init.h>#include <linux/types.h>#include <linux/pci.h>#include <linux/spinlock.h>#include <linux/slab.h>#include <linux/completion.h>#include <linux/dma-mapping.h>#include <linux/blkdev.h>#include <linux/compat.h>#include <linux/delay.h> /* ssleep prototype */#include <linux/kthread.h>#include <linux/uaccess.h>#include <scsi/scsi_host.h> #include "aacraid.h" # define AAC_DEBUG_PREAMBLE	KERN_INFO# define AAC_DEBUG_POSTAMBLE/** *	ioctl_send_fib	-	send a FIB from userspace *	@dev:	adapter is being processed *	@arg:	arguments to the ioctl call * *	This routine sends a fib to the adapter on behalf of a user level *	program. */static int ioctl_send_fib(struct aac_dev * dev, void __user *arg){	struct hw_fib * kfib;	struct fib *fibptr;	struct hw_fib * hw_fib = (struct hw_fib *)0;	dma_addr_t hw_fib_pa = (dma_addr_t)0LL;	unsigned int size, osize;	int retval; 	if (dev->in_reset) {		return -EBUSY;	}	fibptr = aac_fib_alloc(dev);	if(fibptr == NULL) {		return -ENOMEM;	} 	kfib = fibptr->hw_fib_va;	/*	 *	First copy in the header so that we can check the size field.	 */	if (copy_from_user((void *)kfib, arg, sizeof(struct aac_fibhdr))) {		aac_fib_free(fibptr);		return -EFAULT;	}	/*	 *	Since we copy based on the fib header size, make sure that we	 *	will not overrun the buffer when we copy the memory. Return	 *	an error if we would.	 */	osize = size = le16_to_cpu(kfib->header.Size) +		sizeof(struct aac_fibhdr);	if (size < le16_to_cpu(kfib->header.SenderSize))		size = le16_to_cpu(kfib->header.SenderSize);	if (size > dev->max_fib_size) {		dma_addr_t daddr; 		if (size > 2048) {			retval = -EINVAL;			goto cleanup;		} 		kfib = dma_alloc_coherent(&dev->pdev->dev, size, &daddr,					  GFP_KERNEL);		if (!kfib) {			retval = -ENOMEM;			goto cleanup;		} 		/* Highjack the hw_fib */		hw_fib = fibptr->hw_fib_va;		hw_fib_pa = fibptr->hw_fib_pa;		fibptr->hw_fib_va = kfib;		fibptr->hw_fib_pa = daddr;		memset(((char *)kfib) + dev->max_fib_size, 0, size - dev->max_fib_size);		memcpy(kfib, hw_fib, dev->max_fib_size);	} 	if (copy_from_user(kfib, arg, size)) {		retval = -EFAULT;		goto cleanup;	} 	/* Sanity check the second copy */	if ((osize != le16_to_cpu(kfib->header.Size) +		sizeof(struct aac_fibhdr))		|| (size < le16_to_cpu(kfib->header.SenderSize))) {		retval = -EINVAL;		goto cleanup;	} 	if (kfib->header.Command == cpu_to_le16(TakeABreakPt)) {		aac_adapter_interrupt(dev);		/*		 * Since we didn't really send a fib, zero out the state to allow		 * cleanup code not to assert.		 */		kfib->header.XferState = 0;	} else {		retval = aac_fib_send(le16_to_cpu(kfib->header.Command), fibptr,				le16_to_cpu(kfib->header.Size) , FsaNormal,				1, 1, NULL, NULL);		if (retval) {			goto cleanup;		}		if (aac_fib_complete(fibptr) != 0) {			retval = -EINVAL;			goto cleanup;		}	}	/*	 *	Make sure that the size returned by the adapter (which includes	 *	the header) is less than or equal to the size of a fib, so we	 *	don't corrupt application data. Then copy that size to the user	 *	buffer. (Don't try to add the header information again, since it	 *	was already included by the adapter.)	 */ 	retval = 0;	if (copy_to_user(arg, (void *)kfib, size))		retval = -EFAULT;cleanup:	if (hw_fib) {		dma_free_coherent(&dev->pdev->dev, size, kfib,				  fibptr->hw_fib_pa);		fibptr->hw_fib_pa = hw_fib_pa;		fibptr->hw_fib_va = hw_fib;	}	if (retval != -ERESTARTSYS)		aac_fib_free(fibptr);	return retval;} /** *	open_getadapter_fib	-	Get the next fib *	@dev:	adapter is being processed *	@arg:	arguments to the open call * *	This routine will get the next Fib, if available, from the AdapterFibContext *	passed in from the user. */static int open_getadapter_fib(struct aac_dev * dev, void __user *arg){	struct aac_fib_context * fibctx;	int status; 	fibctx = kmalloc_obj(struct aac_fib_context);	if (fibctx == NULL) {		status = -ENOMEM;	} else {		unsigned long flags;		struct list_head * entry;		struct aac_fib_context * context; 		fibctx->type = FSAFS_NTC_GET_ADAPTER_FIB_CONTEXT;		fibctx->size = sizeof(struct aac_fib_context);		/*		 *	Yes yes, I know this could be an index, but we have a		 * better guarantee of uniqueness for the locked loop below.		 * Without the aid of a persistent history, this also helps		 * reduce the chance that the opaque context would be reused.		 */		fibctx->unique = (u32)((ulong)fibctx & 0xFFFFFFFF);		/*		 *	Initialize the mutex used to wait for the next AIF.		 */		init_completion(&fibctx->completion);		fibctx->wait = 0;		/*		 *	Initialize the fibs and set the count of fibs on		 *	the list to 0.		 */		fibctx->count = 0;		INIT_LIST_HEAD(&fibctx->fib_list);		fibctx->jiffies = jiffies/HZ;		/*		 *	Now add this context onto the adapter's		 *	AdapterFibContext list.		 */		spin_lock_irqsave(&dev->fib_lock, flags);		/* Ensure that we have a unique identifier */		entry = dev->fib_list.next;		while (entry != &dev->fib_list) {			context = list_entry(entry, struct aac_fib_context, next);			if (context->unique == fibctx->unique) {				/* Not unique (32 bits) */				fibctx->unique++;				entry = dev->fib_list.next;			} else {				entry = entry->next;			}		}		list_add_tail(&fibctx->next, &dev->fib_list);		spin_unlock_irqrestore(&dev->fib_lock, flags);		if (copy_to_user(arg, &fibctx->unique,						sizeof(fibctx->unique))) {			status = -EFAULT;		} else {			status = 0;		}	}	return status;} struct compat_fib_ioctl {	u32	fibctx;	s32	wait;	compat_uptr_t fib;}; /** *	next_getadapter_fib	-	get the next fib *	@dev: adapter to use *	@arg: ioctl argument * *	This routine will get the next Fib, if available, from the AdapterFibContext *	passed in from the user. */static int next_getadapter_fib(struct aac_dev * dev, void __user *arg){	struct fib_ioctl f;	struct fib *fib;	struct aac_fib_context *fibctx;	int status;	struct list_head * entry;	unsigned long flags; 	if (in_compat_syscall()) {		struct compat_fib_ioctl cf; 		if (copy_from_user(&cf, arg, sizeof(struct compat_fib_ioctl)))			return -EFAULT; 		f.fibctx = cf.fibctx;		f.wait = cf.wait;		f.fib = compat_ptr(cf.fib);	} else {		if (copy_from_user(&f, arg, sizeof(struct fib_ioctl)))			return -EFAULT;	}	/*	 *	Verify that the HANDLE passed in was a valid AdapterFibContext	 *	 *	Search the list of AdapterFibContext addresses on the adapter	 *	to be sure this is a valid address	 */	spin_lock_irqsave(&dev->fib_lock, flags);	entry = dev->fib_list.next;	fibctx = NULL; 	while (entry != &dev->fib_list) {		fibctx = list_entry(entry, struct aac_fib_context, next);		/*		 *	Extract the AdapterFibContext from the Input parameters.		 */		if (fibctx->unique == f.fibctx) { /* We found a winner */			break;		}		entry = entry->next;		fibctx = NULL;	}	if (!fibctx) {		spin_unlock_irqrestore(&dev->fib_lock, flags);		dprintk ((KERN_INFO "Fib Context not found\n"));		return -EINVAL;	} 	if((fibctx->type != FSAFS_NTC_GET_ADAPTER_FIB_CONTEXT) ||		 (fibctx->size != sizeof(struct aac_fib_context))) {		spin_unlock_irqrestore(&dev->fib_lock, flags);		dprintk ((KERN_INFO "Fib Context corrupt?\n"));		return -EINVAL;	}	status = 0;	/*	 *	If there are no fibs to send back, then either wait or return	 *	-EAGAIN	 */return_fib:	if (!list_empty(&fibctx->fib_list)) {		/*		 *	Pull the next fib from the fibs		 */		entry = fibctx->fib_list.next;		list_del(entry); 		fib = list_entry(entry, struct fib, fiblink);		fibctx->count--;		spin_unlock_irqrestore(&dev->fib_lock, flags);		if (copy_to_user(f.fib, fib->hw_fib_va, sizeof(struct hw_fib))) {			kfree(fib->hw_fib_va);			kfree(fib);			return -EFAULT;		}		/*		 *	Free the space occupied by this copy of the fib.		 */		kfree(fib->hw_fib_va);		kfree(fib);		status = 0;	} else {		spin_unlock_irqrestore(&dev->fib_lock, flags);		/* If someone killed the AIF aacraid thread, restart it */		status = !dev->aif_thread;		if (status && !dev->in_reset && dev->queues && dev->fsa_dev) {			/* Be paranoid, be very paranoid! */			kthread_stop(dev->thread);			ssleep(1);			dev->aif_thread = 0;			dev->thread = kthread_run(aac_command_thread, dev,						  "%s", dev->name);			ssleep(1);		}		if (f.wait) {			if (wait_for_completion_interruptible(&fibctx->completion) < 0) {				status = -ERESTARTSYS;			} else {				/* Lock again and retry */				spin_lock_irqsave(&dev->fib_lock, flags);				goto return_fib;			}		} else {			status = -EAGAIN;		}	}	fibctx->jiffies = jiffies/HZ;	return status;} int aac_close_fib_context(struct aac_dev * dev, struct aac_fib_context * fibctx){	struct fib *fib; 	/*	 *	First free any FIBs that have not been consumed.	 */	while (!list_empty(&fibctx->fib_list)) {		struct list_head * entry;		/*		 *	Pull the next fib from the fibs		 */		entry = fibctx->fib_list.next;		list_del(entry);		fib = list_entry(entry, struct fib, fiblink);		fibctx->count--;		/*		 *	Free the space occupied by this copy of the fib.		 */		kfree(fib->hw_fib_va);		kfree(fib);	}	/*	 *	Remove the Context from the AdapterFibContext List	 */	list_del(&fibctx->next);	/*	 *	Invalidate context	 */	fibctx->type = 0;	/*	 *	Free the space occupied by the Context	 */	kfree(fibctx);	return 0;} /** *	close_getadapter_fib	-	close down user fib context *	@dev: adapter *	@arg: ioctl arguments * *	This routine will close down the fibctx passed in from the user. */ static int close_getadapter_fib(struct aac_dev * dev, void __user *arg){	struct aac_fib_context *fibctx;	int status;	unsigned long flags;	struct list_head * entry; 	/*	 *	Verify that the HANDLE passed in was a valid AdapterFibContext	 *	 *	Search the list of AdapterFibContext addresses on the adapter	 *	to be sure this is a valid address	 */ 	entry = dev->fib_list.next;	fibctx = NULL; 	while(entry != &dev->fib_list) {		fibctx = list_entry(entry, struct aac_fib_context, next);		/*		 *	Extract the fibctx from the input parameters		 */		if (fibctx->unique == (u32)(uintptr_t)arg) /* We found a winner */			break;		entry = entry->next;		fibctx = NULL;	} 	if (!fibctx)		return 0; /* Already gone */ 	if((fibctx->type != FSAFS_NTC_GET_ADAPTER_FIB_CONTEXT) ||		 (fibctx->size != sizeof(struct aac_fib_context)))		return -EINVAL;	spin_lock_irqsave(&dev->fib_lock, flags);	status = aac_close_fib_context(dev, fibctx);	spin_unlock_irqrestore(&dev->fib_lock, flags);	return status;} /** *	check_revision	-	close down user fib context *	@dev: adapter *	@arg: ioctl arguments * *	This routine returns the driver version. *	Under Linux, there have been no version incompatibilities, so this is *	simple! */ static int check_revision(struct aac_dev *dev, void __user *arg){	struct revision response;	char *driver_version = aac_driver_version;	u32 version; 	response.compat = 1;	version = (simple_strtol(driver_version,				&driver_version, 10) << 24) | 0x00000400;	version += simple_strtol(driver_version + 1, &driver_version, 10) << 16;	version += simple_strtol(driver_version + 1, NULL, 10);	response.version = cpu_to_le32(version);#	ifdef AAC_DRIVER_BUILD		response.build = cpu_to_le32(AAC_DRIVER_BUILD);#	else		response.build = cpu_to_le32(9999);#	endif 	if (copy_to_user(arg, &response, sizeof(response)))		return -EFAULT;	return 0;}  /** * aac_send_raw_srb() *	@dev:	adapter is being processed *	@arg:	arguments to the send call */static int aac_send_raw_srb(struct aac_dev* dev, void __user * arg){	struct fib* srbfib;	int status;	struct aac_srb *srbcmd = NULL;	struct aac_hba_cmd_req *hbacmd = NULL;	struct user_aac_srb *user_srbcmd = NULL;	struct user_aac_srb __user *user_srb = arg;	struct aac_srb_reply __user *user_reply;	u32 chn;	u32 fibsize = 0;	u32 flags = 0;	s32 rcode = 0;	u32 data_dir;	void __user *sg_user[HBA_MAX_SG_EMBEDDED];	void *sg_list[HBA_MAX_SG_EMBEDDED];	u32 sg_count[HBA_MAX_SG_EMBEDDED];	u32 sg_indx = 0;	u32 byte_count = 0;	u32 actual_fibsize64, actual_fibsize = 0;	int i;	int is_native_device;

Lines 1–500 of 1121.