/*Data struct associated with each of the tapdisk devices*/
typedef struct tap_blkif {
- struct vm_area_struct *vma; /*Shared memory area */
+ struct mm_struct *mm; /*User address space */
unsigned long rings_vstart; /*Kernel memory mapping */
unsigned long user_vstart; /*User memory mapping */
unsigned long dev_inuse; /*One process opens device at a time. */
[req id, idx] tuple */
blkif_t *blkif; /*Associate blkif with tapdev */
struct domid_translate_ext trans; /*Translation from domid to bus. */
+ struct page **map; /*Mapping page */
} tap_blkif_t;
static struct tap_blkif *tapfds[MAX_TAP_DEV];
/******************************************************************
* BLKTAP VM OPS
*/
-struct tap_vma_priv {
- tap_blkif_t *info;
- struct page *map[];
-};
static struct page *blktap_nopage(struct vm_area_struct *vma,
unsigned long address,
pte_t *ptep, int is_fullmm)
{
pte_t copy;
- tap_blkif_t *info;
+ tap_blkif_t *info = NULL;
int offset, seg, usr_idx, pending_idx, mmap_idx;
unsigned long uvstart;
unsigned long kvaddr;
- struct tap_vma_priv *priv;
struct page *pg;
struct grant_handle_pair *khandle;
struct gnttab_unmap_grant_ref unmap[2];
return ptep_get_and_clear_full(vma->vm_mm, uvaddr,
ptep, is_fullmm);
- priv = vma->vm_private_data;
-
/* TODO Should these be changed to if statements? */
BUG_ON(!info);
BUG_ON(!info->idx_map);
- BUG_ON(!priv);
offset = (int) ((uvaddr - uvstart) >> PAGE_SHIFT);
usr_idx = OFFSET_TO_USR_IDX(offset);
kvaddr = idx_to_kaddr(mmap_idx, pending_idx, seg);
pg = pfn_to_page(__pa(kvaddr) >> PAGE_SHIFT);
ClearPageReserved(pg);
- priv->map[offset + RING_PAGES] = NULL;
+ info->map[offset + RING_PAGES] = NULL;
khandle = &pending_handle(mmap_idx, pending_idx, seg);
return copy;
}
+static void blktap_vma_open(struct vm_area_struct *vma)
+{
+ tap_blkif_t *info;
+ if (vma->vm_file == NULL)
+ return;
+
+ info = vma->vm_file->private_data;
+ vma->vm_private_data =
+ &info->map[(vma->vm_start - info->rings_vstart) >> PAGE_SHIFT];
+}
+
+/* tricky part
+ * When partial munmapping, ->open() is called only splitted vma which
+ * will be released soon. * See split_vma() and do_munmap() in mm/mmap.c
+ * So there is no chance to fix up vm_private_data of the end vma.
+ */
static void blktap_vma_close(struct vm_area_struct *vma)
{
- struct tap_vma_priv *priv = vma->vm_private_data;
+ tap_blkif_t *info;
+ struct vm_area_struct *next = vma->vm_next;
- if (priv) {
- priv->info->vma = NULL;
- kfree(priv);
- }
+ if (next == NULL ||
+ vma->vm_ops != next->vm_ops ||
+ vma->vm_end != next->vm_start ||
+ vma->vm_file == NULL ||
+ vma->vm_file != next->vm_file)
+ return;
+
+ info = vma->vm_file->private_data;
+ next->vm_private_data =
+ &info->map[(next->vm_start - info->rings_vstart) >> PAGE_SHIFT];
}
-struct vm_operations_struct blktap_vm_ops = {
+static struct vm_operations_struct blktap_vm_ops = {
nopage: blktap_nopage,
zap_pte: blktap_clear_pte,
+ open: blktap_vma_open,
close: blktap_vma_close,
};
info = tapfds[minor];
/* we could have failed a previous attempt. */
if (!info ||
- ((info->dev_inuse == 0) &&
+ ((!test_bit(0, &info->dev_inuse)) &&
(info->dev_pending == 0)) ) {
info->dev_pending = 1;
goto found;
FRONT_RING_INIT(&info->ufe_ring, sring, PAGE_SIZE);
filp->private_data = info;
- info->vma = NULL;
+ info->mm = NULL;
info->idx_map = kmalloc(sizeof(unsigned long) * MAX_PENDING_REQS,
GFP_KERNEL);
info->ring_ok = 0;
smp_wmb();
- info->dev_inuse = 0;
- DPRINTK("Freeing device [/dev/xen/blktap%d]\n",info->minor);
+ mmput(info->mm);
+ info->mm = NULL;
+ kfree(info->map);
+ info->map = NULL;
/* Free the ring page. */
ClearPageReserved(virt_to_page(info->ufe_ring.sring));
info->status = CLEANSHUTDOWN;
}
+ clear_bit(0, &info->dev_inuse);
+ DPRINTK("Freeing device [/dev/xen/blktap%d]\n",info->minor);
+
return 0;
}
static int blktap_mmap(struct file *filp, struct vm_area_struct *vma)
{
int size;
- struct tap_vma_priv *priv;
tap_blkif_t *info = filp->private_data;
int ret;
}
/* Mark this VM as containing foreign pages, and set up mappings. */
- priv = kzalloc(sizeof(*priv) + ((vma->vm_end - vma->vm_start)
- >> PAGE_SHIFT) * sizeof(*priv->map),
- GFP_KERNEL);
- if (priv == NULL) {
+ info->map = kzalloc(((vma->vm_end - vma->vm_start) >> PAGE_SHIFT) *
+ sizeof(*info->map), GFP_KERNEL);
+ if (info->map == NULL) {
WPRINTK("Couldn't alloc VM_FOREIGN map.\n");
goto fail;
}
- priv->info = info;
- vma->vm_private_data = priv;
+ vma->vm_private_data = info->map;
vma->vm_flags |= VM_FOREIGN;
vma->vm_flags |= VM_DONTCOPY;
vma->vm_mm->context.has_foreign_mappings = 1;
#endif
- info->vma = vma;
+ info->mm = get_task_mm(current);
smp_wmb();
info->ring_ok = 1;
return 0;
wake_up(&pending_free_wq);
}
+static void blktap_zap_page_range(struct mm_struct *mm,
+ unsigned long uvaddr, int nr_pages)
+{
+ unsigned long end = uvaddr + (nr_pages << PAGE_SHIFT);
+ struct vm_area_struct *vma;
+
+ vma = find_vma(mm, uvaddr);
+ while (vma && uvaddr < end) {
+ unsigned long s = max(uvaddr, vma->vm_start);
+ unsigned long e = min(end, vma->vm_end);
+
+ zap_page_range(vma, s, e - s, NULL);
+
+ uvaddr = e;
+ vma = vma->vm_next;
+ }
+}
+
static void fast_flush_area(pending_req_t *req, int k_idx, int u_idx,
int tapidx)
{
return;
}
- mm = info->vma ? info->vma->vm_mm : NULL;
+ mm = info->mm;
- if (info->vma != NULL &&
- xen_feature(XENFEAT_auto_translated_physmap)) {
+ if (mm != NULL && xen_feature(XENFEAT_auto_translated_physmap)) {
down_write(&mm->mmap_sem);
- zap_page_range(info->vma,
- MMAP_VADDR(info->user_vstart, u_idx, 0),
- req->nr_pages << PAGE_SHIFT, NULL);
+ blktap_zap_page_range(mm,
+ MMAP_VADDR(info->user_vstart, u_idx, 0),
+ req->nr_pages);
up_write(&mm->mmap_sem);
return;
}
GNTTABOP_unmap_grant_ref, unmap, invcount);
BUG_ON(ret);
- if (info->vma != NULL &&
- !xen_feature(XENFEAT_auto_translated_physmap)) {
+ if (mm != NULL && !xen_feature(XENFEAT_auto_translated_physmap)) {
if (!locked++)
down_write(&mm->mmap_sem);
- zap_page_range(info->vma,
- MMAP_VADDR(info->user_vstart, u_idx, 0),
- req->nr_pages << PAGE_SHIFT, NULL);
+ blktap_zap_page_range(mm,
+ MMAP_VADDR(info->user_vstart, u_idx, 0),
+ req->nr_pages);
}
if (locked)
for (j = 0; j < pending_req->nr_pages; j++) {
unsigned long kvaddr, uvaddr;
- struct tap_vma_priv *priv = info->vma->vm_private_data;
struct page *pg;
int offset;
pg = pfn_to_page(__pa(kvaddr) >> PAGE_SHIFT);
ClearPageReserved(pg);
offset = (uvaddr - info->rings_vstart) >> PAGE_SHIFT;
- priv->map[offset] = NULL;
+ info->map[offset] = NULL;
}
fast_flush_area(pending_req, pending_idx, usr_idx, info->minor);
info->idx_map[usr_idx] = INVALID_REQ;
info = tapfds[blkif->dev_num];
- if (blkif->dev_num > MAX_TAP_DEV || !info || !info->dev_inuse) {
+ if (blkif->dev_num > MAX_TAP_DEV || !info ||
+ !test_bit(0, &info->dev_inuse)) {
if (print_dbug) {
WPRINTK("Can't get UE info!\n");
print_dbug = 0;
unsigned int nseg;
int ret, i, nr_sects = 0;
tap_blkif_t *info;
- struct tap_vma_priv *priv;
blkif_request_t *target;
int pending_idx = RTN_PEND_IDX(pending_req,pending_req->mem_idx);
int usr_idx;
uint16_t mmap_idx = pending_req->mem_idx;
struct mm_struct *mm;
+ struct vm_area_struct *vma = NULL;
if (blkif->dev_num < 0 || blkif->dev_num > MAX_TAP_DEV)
goto fail_response;
pending_req->status = BLKIF_RSP_OKAY;
pending_req->nr_pages = nseg;
op = 0;
- priv = info->vma->vm_private_data;
- mm = info->vma->vm_mm;
+ mm = info->mm;
if (!xen_feature(XENFEAT_auto_translated_physmap))
down_write(&mm->mmap_sem);
for (i = 0; i < nseg; i++) {
>> PAGE_SHIFT));
offset = (uvaddr - info->rings_vstart) >> PAGE_SHIFT;
pg = pfn_to_page(__pa(kvaddr) >> PAGE_SHIFT);
- priv->map[offset] = pg;
+ info->map[offset] = pg;
}
} else {
for (i = 0; i < nseg; i++) {
offset = (uvaddr - info->rings_vstart) >> PAGE_SHIFT;
pg = pfn_to_page(__pa(kvaddr) >> PAGE_SHIFT);
- priv->map[offset] = pg;
+ info->map[offset] = pg;
}
}
pg = pfn_to_page(__pa(kvaddr) >> PAGE_SHIFT);
SetPageReserved(pg);
if (xen_feature(XENFEAT_auto_translated_physmap)) {
- ret = vm_insert_page(info->vma,
- MMAP_VADDR(info->user_vstart,
- usr_idx, i), pg);
+ unsigned long uvaddr = MMAP_VADDR(info->user_vstart,
+ usr_idx, i);
+ if (vma && uvaddr >= vma->vm_end) {
+ vma = vma->vm_next;
+ if (vma &&
+ (uvaddr < vma->vm_start ||
+ uvaddr >= vma->vm_end))
+ vma = NULL;
+ }
+ if (vma == NULL) {
+ vma = find_vma(mm, uvaddr);
+ /* this virtual area was already munmapped.
+ so skip to next page */
+ if (!vma)
+ continue;
+ }
+ ret = vm_insert_page(vma, uvaddr, pg);
if (ret) {
up_write(&mm->mmap_sem);
goto fail_flush;