DEFINE_SPINLOCK(backdev_io_lock);
static int backdev_major;
+static int umap_uaddr(struct mm_struct *mm, unsigned long address);
int
register_backdev(void)
return 0;
}
+void
+backdev_end_requests(struct tap_blkif *info)
+{
+ int pending_idx, usr_idx, mmap_idx;
+ pending_req_t *pending_req;
+ struct request *req;
+ int ret, i;
+
+ spin_lock_irq(&backdev_io_lock);
+ for (usr_idx = 0; usr_idx < MAX_PENDING_REQS; usr_idx++) {
+ if (info->idx_map[usr_idx] == INVALID_REQ)
+ continue;
+ pending_idx = MASK_PEND_IDX(ID_TO_IDX(info->idx_map[usr_idx]));
+ mmap_idx = ID_TO_MIDX(info->idx_map[usr_idx]);
+ pending_req = &pending_reqs[mmap_idx][pending_idx];
+ blkif_put(info->blkif);
+ if (pending_req->inuse != 2)
+ continue;
+ for (i = 0; i < pending_req->nr_pages; i++)
+ umap_uaddr(&init_mm, idx_to_kaddr(mmap_idx,
+ pending_idx, i));
+ req = (struct request *)(unsigned long)pending_req->id;
+ /* See NOTE in do_backdev_request. */
+ ret = end_that_request_first(req, 1, req->hard_nr_sectors);
+ BUG_ON(ret);
+ end_that_request_last(req, 1);
+ }
+ if (info->backdev) {
+ info->backdev->gd->queue->queuedata = NULL;
+ blk_start_queue(info->backdev->gd->queue);
+ }
+ spin_unlock_irq(&backdev_io_lock);
+}
+
int
destroy_backdev(struct tap_blkif *uinfo)
{
struct backdev_info *info = uinfo->backdev;
- info->destroy = 1;
-
DPRINTK("destroy backdev %d users %d\n", uinfo->minor, info->users);
if (info->users)
return -EBUSY;
blk_cleanup_queue(info->gd->queue);
+ if (uinfo->blkif->xenblkd)
+ wake_up_process(uinfo->blkif->xenblkd);
+
blkif_put(uinfo->blkif);
uinfo->backdev = NULL;
return 0;
}
+int
+backdev_users(struct tap_blkif *uinfo)
+{
+ return uinfo->backdev && (uinfo->backdev->users != 0);
+}
+
static int
backdev_open(struct inode *inode, struct file *filep)
{
struct backdev_info *info = inode->i_bdev->bd_disk->private_data;
+ if (info->uinfo == NULL || info->uinfo->remove_requested)
+ return -ENOENT;
info->users++;
return 0;
}
{
struct backdev_info *info = inode->i_bdev->bd_disk->private_data;
info->users--;
- if (info->destroy)
+ if (info->uinfo->remove_requested)
destroy_backdev(info->uinfo);
return 0;
}
end_request(req, 0);
continue;
}
- if (info->destroy) {
+ if (uinfo->remove_requested) {
DPRINTK("device no longer in use %d\n", info->uinfo->minor);
end_request(req, 0);
continue;
do_backdev_request(request_queue_t *rq)
{
struct backdev_info *info;
+ struct request *req;
info = rq->queuedata;
- if (info->uinfo) {
- info->uinfo->blkif->waiting_reqs = 1;
- wake_up(&info->uinfo->blkif->wq);
- DPRINTK("got requests for dev %d wake %p\n",
- info->uinfo->minor, info->uinfo->blkif);
+ if (info == NULL || info->uinfo == NULL ||
+ info->uinfo->remove_requested) {
+ /* NOTE: we pretend that the request succeeded because
+ * this seems better than returning block device
+ * errors to dom0 given that the linux filesystem code
+ * doesn't seem to handle these too gracefully. */
+ while ((req = elv_next_request(rq)))
+ end_request(req, 1);
+ return;
}
+ info->uinfo->blkif->waiting_reqs = 1;
+ wake_up(&info->uinfo->blkif->wq);
}
void
struct backdev_info {
int users;
- int destroy;
struct gendisk *gd;
struct tap_blkif *uinfo;
};
extern void backdev_finish_req(struct tap_blkif *, int, blkif_response_t *,
struct pending_req *);
extern void backdev_restart_queue(struct tap_blkif *);
+extern int backdev_users(struct tap_blkif *);
+extern void backdev_end_requests(struct tap_blkif *);
#include <linux/poll.h>
#include <linux/delay.h>
#include <asm/tlbflush.h>
+#include <linux/syscalls.h>
#include "blktap.h"
#include "backdev.h"
ClearPageReserved(virt_to_page(info->ufe_ring.sring));
free_page((unsigned long)info->ufe_ring.sring);
- kfree(info->idx_map);
- info->idx_map = NULL;
-
info->vma = NULL;
clear_bit(2, &info->dev_inuse);
+ if (info->blkif->xenblkd)
+ wake_up_process(info->blkif->xenblkd);
up_write(&info->vm_update_sem);
}
if (info && info->trans.domid == domid &&
info->trans.busid == xenbus_id) {
info->blkif = blkif;
- info->status = RUNNING;
return info;
}
}
vma->vm_mm->context.has_foreign_mappings = 1;
#endif
+ info->pid = current->pid;
+ DPRINTK("blktap: mapping pid is %d\n", info->pid);
+
info->vma = vma;
return 0;
return 0;
case BLKTAP_IOCTL_SENDPID:
- if (info) {
- info->pid = (pid_t)arg;
- DPRINTK("blktap: pid received %d\n",
- info->pid);
- }
+ /* deprecated */
return 0;
case BLKTAP_IOCTL_NEWINTF:
wait_event_interruptible(
blkif->wq,
- blkif->waiting_reqs || kthread_should_stop());
+ blkif->waiting_reqs || info->vma == NULL ||
+ (info->remove_requested && backdev_users(info) == 0));
wait_event_interruptible(
pending_free_wq,
- !list_empty(&pending_free) || kthread_should_stop());
-
- if (kthread_should_stop())
- break;
+ !list_empty(&pending_free) || info->vma == NULL ||
+ (info->remove_requested && backdev_users(info) == 0));
blkif->waiting_reqs = 0;
smp_mb(); /* clear flag *before* checking for work */
else
backdev_restart_queue(info);
+ if (info->remove_requested && backdev_users(info) == 0) {
+ sys_kill(info->pid, SIGTERM);
+ info->remove_requested = 0;
+ }
+
up_read(&info->vm_update_sem);
if (log_stats && time_after(jiffies, blkif->st_print))
if (debug_lvl)
printk(KERN_DEBUG "%s: exiting\n", current->comm);
- destroy_backdev(info);
+ backdev_end_requests(info);
+ if (info->backdev)
+ destroy_backdev(info);
+ kfree(info->idx_map);
+ info->idx_map = NULL;
+
+ tap_blkif_free(blkif);
blkif->xenblkd = NULL;
- blkif_put(blkif);
clear_bit(3, &info->dev_inuse);
+ info->remove_requested = 0;
return 0;
}
usr_idx = (int)res.id;
DPRINTK("response %d id %x idx_map %p\n", rc, usr_idx,
info->idx_map);
+ if (usr_idx >= MAX_PENDING_REQS ||
+ info->idx_map[usr_idx] == INVALID_REQ) {
+ WPRINTK("Request %d/%d invalid [%x], tapdisk %d%p\n",
+ rc, rp, usr_idx, info->pid, info->vma);
+ continue;
+ }
pending_idx = MASK_PEND_IDX(ID_TO_IDX(info->idx_map[usr_idx]));
mmap_idx = ID_TO_MIDX(info->idx_map[usr_idx]);
int more_to_do = 0;
int notify;
+ if (!tap_blkif_connected(blkif))
+ return;
+
resp.id = id;
resp.operation = op;
resp.status = st;
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. */
- unsigned long dev_pending; /*In process of being opened */
- unsigned long ring_ok; /*make this ring->state */
blkif_front_ring_t ufe_ring; /*Rings up to user space. */
wait_queue_head_t wait; /*for poll */
int minor; /*Minor number for tapdisk device */
pid_t pid; /*tapdisk process id */
- enum { RUNNING, CLEANSHUTDOWN } status; /*Detect a clean userspace
- shutdown */
unsigned long *idx_map; /*Record the user ring id to kern
[req id, idx] tuple */
blkif_t *blkif; /*Associate blkif with tapdev */
struct domid_translate trans; /*Translation from domid to bus. */
struct backdev_info *backdev; /*Backend domain device info */
+ int remove_requested; /*Xenbus triggered remove requested */
} tap_blkif_t;
/*
#include <linux/kthread.h>
#include <xen/xenbus.h>
#include "common.h"
+#include "blktap.h"
struct backend_info
if (be->backend_watch.node) {
unregister_xenbus_watch(&be->backend_watch);
kfree(be->backend_watch.node);
- be->backend_watch.node = NULL;
}
if (be->blkif) {
- if (be->blkif->xenblkd)
- kthread_stop(be->blkif->xenblkd);
- tap_blkif_free(be->blkif);
- be->blkif = NULL;
+ if (be->blkif->xenblkd == NULL) {
+ if (atomic_read(&be->blkif->refcnt) != 1)
+ WPRINTK("refcnt is %d, expected 1",
+ atomic_read(&be->blkif->refcnt));
+ /* If the kthread was never started, free tap_blkif. */
+ tap_blkif_free(be->blkif);
+ } else {
+ struct tap_blkif *info = be->blkif->tapif;
+
+ down_write(&info->vm_update_sem);
+ tap_blkif_unmap(be->blkif);
+ be->blkif->be = NULL;
+ up_write(&info->vm_update_sem);
+
+ be->blkif->tapif->remove_requested = 1;
+ wake_up_process(be->blkif->xenblkd);
+ blkif_put(be->blkif);
+ }
}
+ be->blkif->be = NULL;
kfree(be);
dev->dev.driver_data = NULL;
return 0;
int tap_blkif_connected(blkif_t *blkif)
{
- return (blkif->be->dev->state == XenbusStateConnected);
+ return (blkif->be && blkif->be->dev->state == XenbusStateConnected);
}
/**