diff options
| -rw-r--r-- | drivers/char/mspec.c | 69 |
1 files changed, 48 insertions, 21 deletions
diff --git a/drivers/char/mspec.c b/drivers/char/mspec.c index c08a4152ee8f..049a46cc9f87 100644 --- a/drivers/char/mspec.c +++ b/drivers/char/mspec.c | |||
| @@ -67,7 +67,7 @@ | |||
| 67 | /* | 67 | /* |
| 68 | * Page types allocated by the device. | 68 | * Page types allocated by the device. |
| 69 | */ | 69 | */ |
| 70 | enum { | 70 | enum mspec_page_type { |
| 71 | MSPEC_FETCHOP = 1, | 71 | MSPEC_FETCHOP = 1, |
| 72 | MSPEC_CACHED, | 72 | MSPEC_CACHED, |
| 73 | MSPEC_UNCACHED | 73 | MSPEC_UNCACHED |
| @@ -83,15 +83,25 @@ static int is_sn2; | |||
| 83 | * One of these structures is allocated when an mspec region is mmaped. The | 83 | * One of these structures is allocated when an mspec region is mmaped. The |
| 84 | * structure is pointed to by the vma->vm_private_data field in the vma struct. | 84 | * structure is pointed to by the vma->vm_private_data field in the vma struct. |
| 85 | * This structure is used to record the addresses of the mspec pages. | 85 | * This structure is used to record the addresses of the mspec pages. |
| 86 | * This structure is shared by all vma's that are split off from the | ||
| 87 | * original vma when split_vma()'s are done. | ||
| 88 | * | ||
| 89 | * The refcnt is incremented atomically because mm->mmap_sem does not | ||
| 90 | * protect in fork case where multiple tasks share the vma_data. | ||
| 86 | */ | 91 | */ |
| 87 | struct vma_data { | 92 | struct vma_data { |
| 88 | atomic_t refcnt; /* Number of vmas sharing the data. */ | 93 | atomic_t refcnt; /* Number of vmas sharing the data. */ |
| 89 | spinlock_t lock; /* Serialize access to the vma. */ | 94 | spinlock_t lock; /* Serialize access to this structure. */ |
| 90 | int count; /* Number of pages allocated. */ | 95 | int count; /* Number of pages allocated. */ |
| 91 | int type; /* Type of pages allocated. */ | 96 | enum mspec_page_type type; /* Type of pages allocated. */ |
| 97 | int flags; /* See VMD_xxx below. */ | ||
| 98 | unsigned long vm_start; /* Original (unsplit) base. */ | ||
| 99 | unsigned long vm_end; /* Original (unsplit) end. */ | ||
| 92 | unsigned long maddr[0]; /* Array of MSPEC addresses. */ | 100 | unsigned long maddr[0]; /* Array of MSPEC addresses. */ |
| 93 | }; | 101 | }; |
| 94 | 102 | ||
| 103 | #define VMD_VMALLOCED 0x1 /* vmalloc'd rather than kmalloc'd */ | ||
| 104 | |||
| 95 | /* used on shub2 to clear FOP cache in the HUB */ | 105 | /* used on shub2 to clear FOP cache in the HUB */ |
| 96 | static unsigned long scratch_page[MAX_NUMNODES]; | 106 | static unsigned long scratch_page[MAX_NUMNODES]; |
| 97 | #define SH2_AMO_CACHE_ENTRIES 4 | 107 | #define SH2_AMO_CACHE_ENTRIES 4 |
| @@ -129,8 +139,8 @@ mspec_zero_block(unsigned long addr, int len) | |||
| 129 | * mspec_open | 139 | * mspec_open |
| 130 | * | 140 | * |
| 131 | * Called when a device mapping is created by a means other than mmap | 141 | * Called when a device mapping is created by a means other than mmap |
| 132 | * (via fork, etc.). Increments the reference count on the underlying | 142 | * (via fork, munmap, etc.). Increments the reference count on the |
| 133 | * mspec data so it is not freed prematurely. | 143 | * underlying mspec data so it is not freed prematurely. |
| 134 | */ | 144 | */ |
| 135 | static void | 145 | static void |
| 136 | mspec_open(struct vm_area_struct *vma) | 146 | mspec_open(struct vm_area_struct *vma) |
| @@ -151,34 +161,44 @@ static void | |||
| 151 | mspec_close(struct vm_area_struct *vma) | 161 | mspec_close(struct vm_area_struct *vma) |
| 152 | { | 162 | { |
| 153 | struct vma_data *vdata; | 163 | struct vma_data *vdata; |
| 154 | int i, pages, result, vdata_size; | 164 | int index, last_index, result; |
| 165 | unsigned long my_page; | ||
| 155 | 166 | ||
| 156 | vdata = vma->vm_private_data; | 167 | vdata = vma->vm_private_data; |
| 157 | if (!atomic_dec_and_test(&vdata->refcnt)) | ||
| 158 | return; | ||
| 159 | 168 | ||
| 160 | pages = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT; | 169 | BUG_ON(vma->vm_start < vdata->vm_start || vma->vm_end > vdata->vm_end); |
| 161 | vdata_size = sizeof(struct vma_data) + pages * sizeof(long); | 170 | |
| 162 | for (i = 0; i < pages; i++) { | 171 | spin_lock(&vdata->lock); |
| 163 | if (vdata->maddr[i] == 0) | 172 | index = (vma->vm_start - vdata->vm_start) >> PAGE_SHIFT; |
| 173 | last_index = (vma->vm_end - vdata->vm_start) >> PAGE_SHIFT; | ||
| 174 | for (; index < last_index; index++) { | ||
| 175 | if (vdata->maddr[index] == 0) | ||
| 164 | continue; | 176 | continue; |
| 165 | /* | 177 | /* |
| 166 | * Clear the page before sticking it back | 178 | * Clear the page before sticking it back |
| 167 | * into the pool. | 179 | * into the pool. |
| 168 | */ | 180 | */ |
| 169 | result = mspec_zero_block(vdata->maddr[i], PAGE_SIZE); | 181 | my_page = vdata->maddr[index]; |
| 182 | vdata->maddr[index] = 0; | ||
| 183 | spin_unlock(&vdata->lock); | ||
| 184 | result = mspec_zero_block(my_page, PAGE_SIZE); | ||
| 170 | if (!result) | 185 | if (!result) |
| 171 | uncached_free_page(vdata->maddr[i]); | 186 | uncached_free_page(my_page); |
| 172 | else | 187 | else |
| 173 | printk(KERN_WARNING "mspec_close(): " | 188 | printk(KERN_WARNING "mspec_close(): " |
| 174 | "failed to zero page %i\n", | 189 | "failed to zero page %i\n", |
| 175 | result); | 190 | result); |
| 191 | spin_lock(&vdata->lock); | ||
| 176 | } | 192 | } |
| 193 | spin_unlock(&vdata->lock); | ||
| 177 | 194 | ||
| 178 | if (vdata_size <= PAGE_SIZE) | 195 | if (!atomic_dec_and_test(&vdata->refcnt)) |
| 179 | kfree(vdata); | 196 | return; |
| 180 | else | 197 | |
| 198 | if (vdata->flags & VMD_VMALLOCED) | ||
| 181 | vfree(vdata); | 199 | vfree(vdata); |
| 200 | else | ||
| 201 | kfree(vdata); | ||
| 182 | } | 202 | } |
| 183 | 203 | ||
| 184 | 204 | ||
| @@ -195,7 +215,8 @@ mspec_nopfn(struct vm_area_struct *vma, unsigned long address) | |||
| 195 | int index; | 215 | int index; |
| 196 | struct vma_data *vdata = vma->vm_private_data; | 216 | struct vma_data *vdata = vma->vm_private_data; |
| 197 | 217 | ||
| 198 | index = (address - vma->vm_start) >> PAGE_SHIFT; | 218 | BUG_ON(address < vdata->vm_start || address >= vdata->vm_end); |
| 219 | index = (address - vdata->vm_start) >> PAGE_SHIFT; | ||
| 199 | maddr = (volatile unsigned long) vdata->maddr[index]; | 220 | maddr = (volatile unsigned long) vdata->maddr[index]; |
| 200 | if (maddr == 0) { | 221 | if (maddr == 0) { |
| 201 | maddr = uncached_alloc_page(numa_node_id()); | 222 | maddr = uncached_alloc_page(numa_node_id()); |
| @@ -237,10 +258,11 @@ static struct vm_operations_struct mspec_vm_ops = { | |||
| 237 | * underlying pages. | 258 | * underlying pages. |
| 238 | */ | 259 | */ |
| 239 | static int | 260 | static int |
| 240 | mspec_mmap(struct file *file, struct vm_area_struct *vma, int type) | 261 | mspec_mmap(struct file *file, struct vm_area_struct *vma, |
| 262 | enum mspec_page_type type) | ||
| 241 | { | 263 | { |
| 242 | struct vma_data *vdata; | 264 | struct vma_data *vdata; |
| 243 | int pages, vdata_size; | 265 | int pages, vdata_size, flags = 0; |
| 244 | 266 | ||
| 245 | if (vma->vm_pgoff != 0) | 267 | if (vma->vm_pgoff != 0) |
| 246 | return -EINVAL; | 268 | return -EINVAL; |
| @@ -255,12 +277,17 @@ mspec_mmap(struct file *file, struct vm_area_struct *vma, int type) | |||
| 255 | vdata_size = sizeof(struct vma_data) + pages * sizeof(long); | 277 | vdata_size = sizeof(struct vma_data) + pages * sizeof(long); |
| 256 | if (vdata_size <= PAGE_SIZE) | 278 | if (vdata_size <= PAGE_SIZE) |
| 257 | vdata = kmalloc(vdata_size, GFP_KERNEL); | 279 | vdata = kmalloc(vdata_size, GFP_KERNEL); |
| 258 | else | 280 | else { |
| 259 | vdata = vmalloc(vdata_size); | 281 | vdata = vmalloc(vdata_size); |
| 282 | flags = VMD_VMALLOCED; | ||
| 283 | } | ||
| 260 | if (!vdata) | 284 | if (!vdata) |
| 261 | return -ENOMEM; | 285 | return -ENOMEM; |
| 262 | memset(vdata, 0, vdata_size); | 286 | memset(vdata, 0, vdata_size); |
| 263 | 287 | ||
| 288 | vdata->vm_start = vma->vm_start; | ||
| 289 | vdata->vm_end = vma->vm_end; | ||
| 290 | vdata->flags = flags; | ||
| 264 | vdata->type = type; | 291 | vdata->type = type; |
| 265 | spin_lock_init(&vdata->lock); | 292 | spin_lock_init(&vdata->lock); |
| 266 | vdata->refcnt = ATOMIC_INIT(1); | 293 | vdata->refcnt = ATOMIC_INIT(1); |
