|
@@ -44,7 +44,7 @@
|
|
|
#include <linux/mutex.h>
|
|
|
#include <linux/scatterlist.h>
|
|
|
#include <linux/bitmap.h>
|
|
|
-#include <linux/llist.h>
|
|
|
+#include <linux/list.h>
|
|
|
|
|
|
#include <xen/xen.h>
|
|
|
#include <xen/xenbus.h>
|
|
@@ -68,13 +68,12 @@ enum blkif_state {
|
|
|
struct grant {
|
|
|
grant_ref_t gref;
|
|
|
unsigned long pfn;
|
|
|
- struct llist_node node;
|
|
|
+ struct list_head node;
|
|
|
};
|
|
|
|
|
|
struct blk_shadow {
|
|
|
struct blkif_request req;
|
|
|
struct request *request;
|
|
|
- unsigned long frame[BLKIF_MAX_SEGMENTS_PER_REQUEST];
|
|
|
struct grant *grants_used[BLKIF_MAX_SEGMENTS_PER_REQUEST];
|
|
|
};
|
|
|
|
|
@@ -105,7 +104,7 @@ struct blkfront_info
|
|
|
struct work_struct work;
|
|
|
struct gnttab_free_callback callback;
|
|
|
struct blk_shadow shadow[BLK_RING_SIZE];
|
|
|
- struct llist_head persistent_gnts;
|
|
|
+ struct list_head persistent_gnts;
|
|
|
unsigned int persistent_gnts_c;
|
|
|
unsigned long shadow_free;
|
|
|
unsigned int feature_flush;
|
|
@@ -165,6 +164,69 @@ static int add_id_to_freelist(struct blkfront_info *info,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static int fill_grant_buffer(struct blkfront_info *info, int num)
|
|
|
+{
|
|
|
+ struct page *granted_page;
|
|
|
+ struct grant *gnt_list_entry, *n;
|
|
|
+ int i = 0;
|
|
|
+
|
|
|
+ while(i < num) {
|
|
|
+ gnt_list_entry = kzalloc(sizeof(struct grant), GFP_NOIO);
|
|
|
+ if (!gnt_list_entry)
|
|
|
+ goto out_of_memory;
|
|
|
+
|
|
|
+ granted_page = alloc_page(GFP_NOIO);
|
|
|
+ if (!granted_page) {
|
|
|
+ kfree(gnt_list_entry);
|
|
|
+ goto out_of_memory;
|
|
|
+ }
|
|
|
+
|
|
|
+ gnt_list_entry->pfn = page_to_pfn(granted_page);
|
|
|
+ gnt_list_entry->gref = GRANT_INVALID_REF;
|
|
|
+ list_add(&gnt_list_entry->node, &info->persistent_gnts);
|
|
|
+ i++;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+
|
|
|
+out_of_memory:
|
|
|
+ list_for_each_entry_safe(gnt_list_entry, n,
|
|
|
+ &info->persistent_gnts, node) {
|
|
|
+ list_del(&gnt_list_entry->node);
|
|
|
+ __free_page(pfn_to_page(gnt_list_entry->pfn));
|
|
|
+ kfree(gnt_list_entry);
|
|
|
+ i--;
|
|
|
+ }
|
|
|
+ BUG_ON(i != 0);
|
|
|
+ return -ENOMEM;
|
|
|
+}
|
|
|
+
|
|
|
+static struct grant *get_grant(grant_ref_t *gref_head,
|
|
|
+ struct blkfront_info *info)
|
|
|
+{
|
|
|
+ struct grant *gnt_list_entry;
|
|
|
+ unsigned long buffer_mfn;
|
|
|
+
|
|
|
+ BUG_ON(list_empty(&info->persistent_gnts));
|
|
|
+ gnt_list_entry = list_first_entry(&info->persistent_gnts, struct grant,
|
|
|
+ node);
|
|
|
+ list_del(&gnt_list_entry->node);
|
|
|
+
|
|
|
+ if (gnt_list_entry->gref != GRANT_INVALID_REF) {
|
|
|
+ info->persistent_gnts_c--;
|
|
|
+ return gnt_list_entry;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Assign a gref to this page */
|
|
|
+ gnt_list_entry->gref = gnttab_claim_grant_reference(gref_head);
|
|
|
+ BUG_ON(gnt_list_entry->gref == -ENOSPC);
|
|
|
+ buffer_mfn = pfn_to_mfn(gnt_list_entry->pfn);
|
|
|
+ gnttab_grant_foreign_access_ref(gnt_list_entry->gref,
|
|
|
+ info->xbdev->otherend_id,
|
|
|
+ buffer_mfn, 0);
|
|
|
+ return gnt_list_entry;
|
|
|
+}
|
|
|
+
|
|
|
static const char *op_name(int op)
|
|
|
{
|
|
|
static const char *const names[] = {
|
|
@@ -293,7 +355,6 @@ static int blkif_ioctl(struct block_device *bdev, fmode_t mode,
|
|
|
static int blkif_queue_request(struct request *req)
|
|
|
{
|
|
|
struct blkfront_info *info = req->rq_disk->private_data;
|
|
|
- unsigned long buffer_mfn;
|
|
|
struct blkif_request *ring_req;
|
|
|
unsigned long id;
|
|
|
unsigned int fsect, lsect;
|
|
@@ -306,7 +367,6 @@ static int blkif_queue_request(struct request *req)
|
|
|
*/
|
|
|
bool new_persistent_gnts;
|
|
|
grant_ref_t gref_head;
|
|
|
- struct page *granted_page;
|
|
|
struct grant *gnt_list_entry = NULL;
|
|
|
struct scatterlist *sg;
|
|
|
|
|
@@ -370,41 +430,8 @@ static int blkif_queue_request(struct request *req)
|
|
|
fsect = sg->offset >> 9;
|
|
|
lsect = fsect + (sg->length >> 9) - 1;
|
|
|
|
|
|
- if (info->persistent_gnts_c) {
|
|
|
- BUG_ON(llist_empty(&info->persistent_gnts));
|
|
|
- gnt_list_entry = llist_entry(
|
|
|
- llist_del_first(&info->persistent_gnts),
|
|
|
- struct grant, node);
|
|
|
-
|
|
|
- ref = gnt_list_entry->gref;
|
|
|
- buffer_mfn = pfn_to_mfn(gnt_list_entry->pfn);
|
|
|
- info->persistent_gnts_c--;
|
|
|
- } else {
|
|
|
- ref = gnttab_claim_grant_reference(&gref_head);
|
|
|
- BUG_ON(ref == -ENOSPC);
|
|
|
-
|
|
|
- gnt_list_entry =
|
|
|
- kmalloc(sizeof(struct grant),
|
|
|
- GFP_ATOMIC);
|
|
|
- if (!gnt_list_entry)
|
|
|
- return -ENOMEM;
|
|
|
-
|
|
|
- granted_page = alloc_page(GFP_ATOMIC);
|
|
|
- if (!granted_page) {
|
|
|
- kfree(gnt_list_entry);
|
|
|
- return -ENOMEM;
|
|
|
- }
|
|
|
-
|
|
|
- gnt_list_entry->pfn =
|
|
|
- page_to_pfn(granted_page);
|
|
|
- gnt_list_entry->gref = ref;
|
|
|
-
|
|
|
- buffer_mfn = pfn_to_mfn(page_to_pfn(
|
|
|
- granted_page));
|
|
|
- gnttab_grant_foreign_access_ref(ref,
|
|
|
- info->xbdev->otherend_id,
|
|
|
- buffer_mfn, 0);
|
|
|
- }
|
|
|
+ gnt_list_entry = get_grant(&gref_head, info);
|
|
|
+ ref = gnt_list_entry->gref;
|
|
|
|
|
|
info->shadow[id].grants_used[i] = gnt_list_entry;
|
|
|
|
|
@@ -435,7 +462,6 @@ static int blkif_queue_request(struct request *req)
|
|
|
kunmap_atomic(shared_data);
|
|
|
}
|
|
|
|
|
|
- info->shadow[id].frame[i] = mfn_to_pfn(buffer_mfn);
|
|
|
ring_req->u.rw.seg[i] =
|
|
|
(struct blkif_request_segment) {
|
|
|
.gref = ref,
|
|
@@ -790,9 +816,8 @@ static void blkif_restart_queue(struct work_struct *work)
|
|
|
|
|
|
static void blkif_free(struct blkfront_info *info, int suspend)
|
|
|
{
|
|
|
- struct llist_node *all_gnts;
|
|
|
- struct grant *persistent_gnt, *tmp;
|
|
|
- struct llist_node *n;
|
|
|
+ struct grant *persistent_gnt;
|
|
|
+ struct grant *n;
|
|
|
|
|
|
/* Prevent new requests being issued until we fix things up. */
|
|
|
spin_lock_irq(&info->io_lock);
|
|
@@ -803,22 +828,20 @@ static void blkif_free(struct blkfront_info *info, int suspend)
|
|
|
blk_stop_queue(info->rq);
|
|
|
|
|
|
/* Remove all persistent grants */
|
|
|
- if (info->persistent_gnts_c) {
|
|
|
- all_gnts = llist_del_all(&info->persistent_gnts);
|
|
|
- persistent_gnt = llist_entry(all_gnts, typeof(*(persistent_gnt)), node);
|
|
|
- while (persistent_gnt) {
|
|
|
- gnttab_end_foreign_access(persistent_gnt->gref, 0, 0UL);
|
|
|
+ if (!list_empty(&info->persistent_gnts)) {
|
|
|
+ list_for_each_entry_safe(persistent_gnt, n,
|
|
|
+ &info->persistent_gnts, node) {
|
|
|
+ list_del(&persistent_gnt->node);
|
|
|
+ if (persistent_gnt->gref != GRANT_INVALID_REF) {
|
|
|
+ gnttab_end_foreign_access(persistent_gnt->gref,
|
|
|
+ 0, 0UL);
|
|
|
+ info->persistent_gnts_c--;
|
|
|
+ }
|
|
|
__free_page(pfn_to_page(persistent_gnt->pfn));
|
|
|
- tmp = persistent_gnt;
|
|
|
- n = persistent_gnt->node.next;
|
|
|
- if (n)
|
|
|
- persistent_gnt = llist_entry(n, typeof(*(persistent_gnt)), node);
|
|
|
- else
|
|
|
- persistent_gnt = NULL;
|
|
|
- kfree(tmp);
|
|
|
+ kfree(persistent_gnt);
|
|
|
}
|
|
|
- info->persistent_gnts_c = 0;
|
|
|
}
|
|
|
+ BUG_ON(info->persistent_gnts_c != 0);
|
|
|
|
|
|
/* No more gnttab callback work. */
|
|
|
gnttab_cancel_free_callback(&info->callback);
|
|
@@ -875,7 +898,7 @@ static void blkif_completion(struct blk_shadow *s, struct blkfront_info *info,
|
|
|
}
|
|
|
/* Add the persistent grant into the list of free grants */
|
|
|
for (i = 0; i < s->req.u.rw.nr_segments; i++) {
|
|
|
- llist_add(&s->grants_used[i]->node, &info->persistent_gnts);
|
|
|
+ list_add(&s->grants_used[i]->node, &info->persistent_gnts);
|
|
|
info->persistent_gnts_c++;
|
|
|
}
|
|
|
}
|
|
@@ -1013,6 +1036,12 @@ static int setup_blkring(struct xenbus_device *dev,
|
|
|
|
|
|
sg_init_table(info->sg, BLKIF_MAX_SEGMENTS_PER_REQUEST);
|
|
|
|
|
|
+ /* Allocate memory for grants */
|
|
|
+ err = fill_grant_buffer(info, BLK_RING_SIZE *
|
|
|
+ BLKIF_MAX_SEGMENTS_PER_REQUEST);
|
|
|
+ if (err)
|
|
|
+ goto fail;
|
|
|
+
|
|
|
err = xenbus_grant_ring(dev, virt_to_mfn(info->ring.sring));
|
|
|
if (err < 0) {
|
|
|
free_page((unsigned long)sring);
|
|
@@ -1171,7 +1200,7 @@ static int blkfront_probe(struct xenbus_device *dev,
|
|
|
spin_lock_init(&info->io_lock);
|
|
|
info->xbdev = dev;
|
|
|
info->vdevice = vdevice;
|
|
|
- init_llist_head(&info->persistent_gnts);
|
|
|
+ INIT_LIST_HEAD(&info->persistent_gnts);
|
|
|
info->persistent_gnts_c = 0;
|
|
|
info->connected = BLKIF_STATE_DISCONNECTED;
|
|
|
INIT_WORK(&info->work, blkif_restart_queue);
|
|
@@ -1203,11 +1232,10 @@ static int blkif_recover(struct blkfront_info *info)
|
|
|
int j;
|
|
|
|
|
|
/* Stage 1: Make a safe copy of the shadow state. */
|
|
|
- copy = kmalloc(sizeof(info->shadow),
|
|
|
+ copy = kmemdup(info->shadow, sizeof(info->shadow),
|
|
|
GFP_NOIO | __GFP_REPEAT | __GFP_HIGH);
|
|
|
if (!copy)
|
|
|
return -ENOMEM;
|
|
|
- memcpy(copy, info->shadow, sizeof(info->shadow));
|
|
|
|
|
|
/* Stage 2: Set up free list. */
|
|
|
memset(&info->shadow, 0, sizeof(info->shadow));
|
|
@@ -1236,7 +1264,7 @@ static int blkif_recover(struct blkfront_info *info)
|
|
|
gnttab_grant_foreign_access_ref(
|
|
|
req->u.rw.seg[j].gref,
|
|
|
info->xbdev->otherend_id,
|
|
|
- pfn_to_mfn(info->shadow[req->u.rw.id].frame[j]),
|
|
|
+ pfn_to_mfn(copy[i].grants_used[j]->pfn),
|
|
|
0);
|
|
|
}
|
|
|
info->shadow[req->u.rw.id].req = *req;
|