diff options
| author | Suren A. Chilingaryan <csa@dside.dyndns.org> | 2011-07-18 00:58:02 +0200 | 
|---|---|---|
| committer | Suren A. Chilingaryan <csa@dside.dyndns.org> | 2011-07-18 00:58:02 +0200 | 
| commit | 71c759e3fa6fb725c51e3800947848cd549222bf (patch) | |
| tree | 3ed1cdbfd3045d21bc74e76d69b759e1bbb9e1fb /driver | |
| parent | 1f133e363c89e736a4221a3dda800e90a706056a (diff) | |
| download | ipecamera-71c759e3fa6fb725c51e3800947848cd549222bf.tar.gz ipecamera-71c759e3fa6fb725c51e3800947848cd549222bf.tar.bz2 ipecamera-71c759e3fa6fb725c51e3800947848cd549222bf.tar.xz ipecamera-71c759e3fa6fb725c51e3800947848cd549222bf.zip  | |
Prevent driver holding hardware locks from unloading
Diffstat (limited to 'driver')
| -rw-r--r-- | driver/base.c | 20 | ||||
| -rw-r--r-- | driver/common.h | 4 | ||||
| -rw-r--r-- | driver/kmem.c | 45 | 
3 files changed, 58 insertions, 11 deletions
diff --git a/driver/base.c b/driver/base.c index d88dcf2..7f4ccad 100644 --- a/driver/base.c +++ b/driver/base.c @@ -495,6 +495,22 @@ static struct file_operations pcidriver_fops = {  	.release = pcidriver_release,  }; +void pcidriver_module_get(pcidriver_privdata_t *privdata) { +    try_module_get(THIS_MODULE); +    atomic_inc(&(privdata->refs)); +//    mod_info("Ref: %i\n", atomic_read(&(privdata->refs))); +} + +void pcidriver_module_put(pcidriver_privdata_t *privdata) { +    if (atomic_add_negative(-1, &(privdata->refs))) { +	atomic_inc(&(privdata->refs)); +	mod_info("Reference counting error..."); +    } else { +	module_put(THIS_MODULE); +//	mod_info("Unref: %i\n", atomic_read(&(privdata->refs))); +    } +} +  /**   *   * Called when an application open()s a /dev/fpga*, attaches the private data @@ -509,6 +525,8 @@ int pcidriver_open(struct inode *inode, struct file *filp)  	privdata = container_of( inode->i_cdev, pcidriver_privdata_t, cdev);  	filp->private_data = privdata; +	pcidriver_module_get(privdata); +  	return 0;  } @@ -525,6 +543,8 @@ int pcidriver_release(struct inode *inode, struct file *filp)  	/* Get the private data area */  	privdata = filp->private_data; +	pcidriver_module_put(privdata); +  	return 0;  } diff --git a/driver/common.h b/driver/common.h index 3d26a97..5de501c 100644 --- a/driver/common.h +++ b/driver/common.h @@ -71,9 +71,13 @@ typedef struct  {  	atomic_t umem_count;				/* id for next umem entry */  	int msi_mode;					/* Flag specifying if interrupt have been initialized in MSI mode */ +	atomic_t refs;					/* Reference counter */  } pcidriver_privdata_t; +void pcidriver_module_get(pcidriver_privdata_t *privdata); +void pcidriver_module_put(pcidriver_privdata_t *privdata); +  /*************************************************************************/  /* Some nice defines that make code more readable */  /* This is to print nice info in the log */ diff --git a/driver/kmem.c b/driver/kmem.c index 31fc685..274ab9f 100644 --- a/driver/kmem.c +++ b/driver/kmem.c @@ -70,7 +70,12 @@ int pcidriver_kmem_alloc(pcidriver_privdata_t *privdata, kmem_handle_t *kmem_han  		if (kmem_entry->mode&KMEM_MODE_PERSISTENT) kmem_handle->flags |= KMEM_FLAG_REUSED_PERSISTENT;  		kmem_entry->mode += 1; -		if (flags&KMEM_FLAG_HW) kmem_entry->refs |= KMEM_REF_HW; +		if (flags&KMEM_FLAG_HW) { +		    if ((kmem_entry->refs&KMEM_REF_HW)==0) +			pcidriver_module_get(privdata); +			 +		    kmem_entry->refs |= KMEM_REF_HW; +		}  		if (flags&KMEM_FLAG_PERSISTENT) kmem_entry->mode |= KMEM_MODE_PERSISTENT;  		privdata->kmem_cur_id = kmem_entry->id; @@ -133,7 +138,11 @@ int pcidriver_kmem_alloc(pcidriver_privdata_t *privdata, kmem_handle_t *kmem_han  	}  	kmem_entry->refs = 0; -	if (kmem_handle->flags&KMEM_FLAG_HW) kmem_entry->refs |= KMEM_REF_HW; +	if (kmem_handle->flags&KMEM_FLAG_HW) { +	    pcidriver_module_get(privdata); + +	    kmem_entry->refs |= KMEM_REF_HW; +	}          kmem_handle->flags = 0; @@ -156,9 +165,13 @@ static int pcidriver_kmem_free_check(pcidriver_privdata_t *privdata, kmem_handle  	if ((kmem_handle->flags & KMEM_FLAG_FORCE) == 0) {  	    if (kmem_entry->mode&KMEM_MODE_COUNT)  		kmem_entry->mode -= 1; -	 -	    if (kmem_handle->flags&KMEM_FLAG_HW) + +	    if (kmem_handle->flags&KMEM_FLAG_HW) { +		if (kmem_entry->refs&KMEM_REF_HW)  +		    pcidriver_module_put(privdata); +  		kmem_entry->refs &= ~KMEM_REF_HW; +	    }  	    if (kmem_handle->flags&KMEM_FLAG_PERSISTENT)  		kmem_entry->mode &= ~KMEM_MODE_PERSISTENT; @@ -167,18 +180,26 @@ static int pcidriver_kmem_free_check(pcidriver_privdata_t *privdata, kmem_handle  		return 0;  	    if (kmem_entry->refs) { -		mod_info("can't free referenced kmem_entry\n");  		kmem_entry->mode += 1; +		mod_info("can't free referenced kmem_entry\n");  		return -EBUSY;  	    }  	    if (kmem_entry->mode & KMEM_MODE_PERSISTENT) { +		kmem_entry->mode += 1;  		mod_info("can't free persistent kmem_entry\n");  		return -EBUSY;  	    }  	    if (((kmem_entry->mode&KMEM_MODE_EXCLUSIVE)==0)&&(kmem_entry->mode&KMEM_MODE_COUNT)&&((kmem_handle->flags&KMEM_FLAG_EXCLUSIVE)==0))   		return 0; +	} else { +	    if (kmem_entry->refs&KMEM_REF_HW) +		    pcidriver_module_put(privdata); +		 +	    while (!atomic_add_negative(-1, &(privdata->refs))) pcidriver_module_put(privdata); +	    atomic_inc(&(privdata->refs)); +		  	}  	return 1; @@ -245,24 +266,24 @@ int pcidriver_kmem_free( pcidriver_privdata_t *privdata, kmem_handle_t *kmem_han   */  int pcidriver_kmem_free_all(pcidriver_privdata_t *privdata)  { -	int failed = 0; +//	int failed = 0;  	struct list_head *ptr, *next;  	pcidriver_kmem_entry_t *kmem_entry;  	/* iterate safely over the entries and delete them */  	list_for_each_safe(ptr, next, &(privdata->kmem_list)) {  		kmem_entry = list_entry(ptr, pcidriver_kmem_entry_t, list); -/*		if (kmem_entry->refs) +		/*if (kmem_entry->refs)  			failed = 1;  		else*/  			pcidriver_kmem_free_entry(privdata, kmem_entry); 		/* spin lock inside! */  	} -	 +/*	  	if (failed) {  		mod_info("Some kmem_entries are still referenced\n");  		return -EBUSY;  	}	 - +*/  	return 0;  } @@ -473,8 +494,9 @@ void pcidriver_kmem_mmap_close(struct vm_area_struct *vma) {  	vma_size = (vma->vm_end - vma->vm_start); -	if (kmem_entry->refs&KMEM_REF_COUNT) -    	kmem_entry->refs -= vma_size / PAGE_SIZE; +	if (kmem_entry->refs&KMEM_REF_COUNT) { +	    kmem_entry->refs -= vma_size / PAGE_SIZE; +	}      }  } @@ -523,6 +545,7 @@ int pcidriver_mmap_kmem(pcidriver_privdata_t *privdata, struct vm_area_struct *v  		mod_info("maximal amount of references is reached by kmem_entry\n");  		return -EBUSY;  	} +	  	kmem_entry->refs += vma_size / PAGE_SIZE;  	vma->vm_flags |= (VM_RESERVED);  | 
