summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSuren A. Chilingaryan <csa@dside.dyndns.org>2011-07-18 00:58:02 +0200
committerSuren A. Chilingaryan <csa@dside.dyndns.org>2011-07-18 00:58:02 +0200
commit71c759e3fa6fb725c51e3800947848cd549222bf (patch)
tree3ed1cdbfd3045d21bc74e76d69b759e1bbb9e1fb
parent1f133e363c89e736a4221a3dda800e90a706056a (diff)
downloadpcitool-71c759e3fa6fb725c51e3800947848cd549222bf.tar.gz
pcitool-71c759e3fa6fb725c51e3800947848cd549222bf.tar.bz2
pcitool-71c759e3fa6fb725c51e3800947848cd549222bf.tar.xz
pcitool-71c759e3fa6fb725c51e3800947848cd549222bf.zip
Prevent driver holding hardware locks from unloading
-rw-r--r--driver/base.c20
-rw-r--r--driver/common.h4
-rw-r--r--driver/kmem.c45
-rwxr-xr-xtests/cleanup.sh2
4 files changed, 59 insertions, 12 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);
diff --git a/tests/cleanup.sh b/tests/cleanup.sh
index 3dd201e..5ef568e 100755
--- a/tests/cleanup.sh
+++ b/tests/cleanup.sh
@@ -10,4 +10,4 @@ pci -w dma1r_reset_request 1
pci -w dma1r_reset 1
pci -r dma1r_running
-pci --clean-kernel-memory dma
+pci --free-kernel-memory dma