xref: /openbmc/qemu/block/qed-cluster.c (revision 39164c13)
1 /*
2  * QEMU Enhanced Disk Format Cluster functions
3  *
4  * Copyright IBM, Corp. 2010
5  *
6  * Authors:
7  *  Stefan Hajnoczi   <stefanha@linux.vnet.ibm.com>
8  *  Anthony Liguori   <aliguori@us.ibm.com>
9  *
10  * This work is licensed under the terms of the GNU LGPL, version 2 or later.
11  * See the COPYING.LIB file in the top-level directory.
12  *
13  */
14 
15 #include "qemu/osdep.h"
16 #include "qed.h"
17 
18 /**
19  * Count the number of contiguous data clusters
20  *
21  * @s:              QED state
22  * @table:          L2 table
23  * @index:          First cluster index
24  * @n:              Maximum number of clusters
25  * @offset:         Set to first cluster offset
26  *
27  * This function scans tables for contiguous clusters.  A contiguous run of
28  * clusters may be allocated, unallocated, or zero.
29  */
30 static unsigned int qed_count_contiguous_clusters(BDRVQEDState *s,
31                                                   QEDTable *table,
32                                                   unsigned int index,
33                                                   unsigned int n,
34                                                   uint64_t *offset)
35 {
36     unsigned int end = MIN(index + n, s->table_nelems);
37     uint64_t last = table->offsets[index];
38     unsigned int i;
39 
40     *offset = last;
41 
42     for (i = index + 1; i < end; i++) {
43         if (qed_offset_is_unalloc_cluster(last)) {
44             /* Counting unallocated clusters */
45             if (!qed_offset_is_unalloc_cluster(table->offsets[i])) {
46                 break;
47             }
48         } else if (qed_offset_is_zero_cluster(last)) {
49             /* Counting zero clusters */
50             if (!qed_offset_is_zero_cluster(table->offsets[i])) {
51                 break;
52             }
53         } else {
54             /* Counting allocated clusters */
55             if (table->offsets[i] != last + s->header.cluster_size) {
56                 break;
57             }
58             last = table->offsets[i];
59         }
60     }
61     return i - index;
62 }
63 
64 typedef struct {
65     BDRVQEDState *s;
66     uint64_t pos;
67     size_t len;
68 
69     QEDRequest *request;
70 
71     /* User callback */
72     QEDFindClusterFunc *cb;
73     void *opaque;
74 } QEDFindClusterCB;
75 
76 static void qed_find_cluster_cb(void *opaque, int ret)
77 {
78     QEDFindClusterCB *find_cluster_cb = opaque;
79     BDRVQEDState *s = find_cluster_cb->s;
80     QEDRequest *request = find_cluster_cb->request;
81     uint64_t offset = 0;
82     size_t len = 0;
83     unsigned int index;
84     unsigned int n;
85 
86     qed_acquire(s);
87     if (ret) {
88         goto out;
89     }
90 
91     index = qed_l2_index(s, find_cluster_cb->pos);
92     n = qed_bytes_to_clusters(s,
93                               qed_offset_into_cluster(s, find_cluster_cb->pos) +
94                               find_cluster_cb->len);
95     n = qed_count_contiguous_clusters(s, request->l2_table->table,
96                                       index, n, &offset);
97 
98     if (qed_offset_is_unalloc_cluster(offset)) {
99         ret = QED_CLUSTER_L2;
100     } else if (qed_offset_is_zero_cluster(offset)) {
101         ret = QED_CLUSTER_ZERO;
102     } else if (qed_check_cluster_offset(s, offset)) {
103         ret = QED_CLUSTER_FOUND;
104     } else {
105         ret = -EINVAL;
106     }
107 
108     len = MIN(find_cluster_cb->len, n * s->header.cluster_size -
109               qed_offset_into_cluster(s, find_cluster_cb->pos));
110 
111 out:
112     find_cluster_cb->cb(find_cluster_cb->opaque, ret, offset, len);
113     qed_release(s);
114     g_free(find_cluster_cb);
115 }
116 
117 /**
118  * Find the offset of a data cluster
119  *
120  * @s:          QED state
121  * @request:    L2 cache entry
122  * @pos:        Byte position in device
123  * @len:        Number of bytes
124  * @cb:         Completion function
125  * @opaque:     User data for completion function
126  *
127  * This function translates a position in the block device to an offset in the
128  * image file.  It invokes the cb completion callback to report back the
129  * translated offset or unallocated range in the image file.
130  *
131  * If the L2 table exists, request->l2_table points to the L2 table cache entry
132  * and the caller must free the reference when they are finished.  The cache
133  * entry is exposed in this way to avoid callers having to read the L2 table
134  * again later during request processing.  If request->l2_table is non-NULL it
135  * will be unreferenced before taking on the new cache entry.
136  */
137 void qed_find_cluster(BDRVQEDState *s, QEDRequest *request, uint64_t pos,
138                       size_t len, QEDFindClusterFunc *cb, void *opaque)
139 {
140     QEDFindClusterCB *find_cluster_cb;
141     uint64_t l2_offset;
142 
143     /* Limit length to L2 boundary.  Requests are broken up at the L2 boundary
144      * so that a request acts on one L2 table at a time.
145      */
146     len = MIN(len, (((pos >> s->l1_shift) + 1) << s->l1_shift) - pos);
147 
148     l2_offset = s->l1_table->offsets[qed_l1_index(s, pos)];
149     if (qed_offset_is_unalloc_cluster(l2_offset)) {
150         cb(opaque, QED_CLUSTER_L1, 0, len);
151         return;
152     }
153     if (!qed_check_table_offset(s, l2_offset)) {
154         cb(opaque, -EINVAL, 0, 0);
155         return;
156     }
157 
158     find_cluster_cb = g_malloc(sizeof(*find_cluster_cb));
159     find_cluster_cb->s = s;
160     find_cluster_cb->pos = pos;
161     find_cluster_cb->len = len;
162     find_cluster_cb->cb = cb;
163     find_cluster_cb->opaque = opaque;
164     find_cluster_cb->request = request;
165 
166     qed_read_l2_table(s, request, l2_offset,
167                       qed_find_cluster_cb, find_cluster_cb);
168 }
169