From 71c759e3fa6fb725c51e3800947848cd549222bf Mon Sep 17 00:00:00 2001 From: "Suren A. Chilingaryan" Date: Mon, 18 Jul 2011 00:58:02 +0200 Subject: Prevent driver holding hardware locks from unloading --- driver/base.c | 20 ++++++++++++++++++++ driver/common.h | 4 ++++ driver/kmem.c | 45 ++++++++++++++++++++++++++++++++++----------- tests/cleanup.sh | 2 +- 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 -- cgit v1.2.3