Statistics
| Branch: | Revision:

root / drivers / tapdisk-filter.c @ abdb293f

History | View | Annotate | Download (6.4 kB)

1
/*
2
 * Copyright (c) 2007, XenSource Inc.
3
 * Copyright (c) 2010, Citrix Systems, Inc.
4
 *
5
 * All rights reserved.
6
 *
7
 * Redistribution and use in source and binary forms, with or without
8
 * modification, are permitted provided that the following conditions are met:
9
 *     * Redistributions of source code must retain the above copyright
10
 *       notice, this list of conditions and the following disclaimer.
11
 *     * Redistributions in binary form must reproduce the above copyright
12
 *       notice, this list of conditions and the following disclaimer in the
13
 *       documentation and/or other materials provided with the distribution.
14
 *     * Neither the name of XenSource Inc. nor the names of its contributors
15
 *       may be used to endorse or promote products derived from this software
16
 *       without specific prior written permission.
17
 *
18
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
22
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
 */
30

    
31
#ifdef HAVE_CONFIG_H
32
#include "config.h"
33
#endif
34

    
35
#include <stdlib.h>
36
#include <unistd.h>
37
#include <libaio.h>
38
#include <syslog.h>
39
#include <sys/time.h>
40

    
41
#include "tapdisk-log.h"
42
#include "tapdisk-filter.h"
43

    
44
#define RSEED      7
45
#define PRE_CHECK  0
46
#define POST_CHECK 1
47

    
48
#define WRITE_INTEGRITY   "buffer integrity failure after write"
49
#define READ_INTEGRITY    "disk integrity failure after read"
50

    
51
#define DBG(f, a...) tlog_write(TLOG_WARN, f, ##a)
52

    
53
/*
54
 * simulate IO errors by knocking request size to zero before
55
 * submitting and restoring original size before returning
56
 */
57
static inline void
58
inject_fault(struct tfilter *filter, struct iocb *io)
59
{
60
        struct fiocb *fio;
61

    
62
        if (!filter->ffree)
63
                return;
64

    
65
        fio = filter->flist[--filter->ffree];
66

    
67
        fio->bytes     = io->u.c.nbytes;
68
        fio->data      = io->data;
69
        io->u.c.nbytes = 0;
70
        io->data       = fio;
71
}
72

    
73
static inline int
74
fault_injected(struct tfilter *filter, struct iocb *io)
75
{
76
        unsigned long iop   = (unsigned long)io->data;
77
        unsigned long start = (unsigned long)filter->fiocbs;
78
        unsigned long end   = start + (filter->iocbs * sizeof(struct fiocb));
79

    
80
        return (iop >= start && iop < end);
81
}
82

    
83
static inline void
84
recover_fault(struct tfilter *filter, struct iocb *io)
85
{
86
        struct fiocb *fio = (struct fiocb *)io->data;
87

    
88
        io->u.c.nbytes = fio->bytes;
89
        io->data       = fio->data;
90

    
91
        memset(fio, 0, sizeof(struct fiocb));
92
        filter->flist[filter->ffree++] = fio;
93
}
94

    
95
static inline uint64_t
96
chksum(char *buf)
97
{
98
        int i, num   = 512 >> 3;
99
        uint64_t *p  = (uint64_t *)buf;
100
        uint64_t sum = 0;
101

    
102
        for (i = 0; i < num; i++)
103
                sum += p[i];
104

    
105
        return sum;
106
}
107

    
108
static inline void
109
check_hash(struct tfilter *filter, uint64_t sec, char *buf, char *type)
110
{
111
        uint64_t sum;
112
        struct dhash *hash;
113

    
114
        hash = filter->dhash + sec;
115
        if (!hash->time.tv_sec)
116
                return;
117

    
118
        sum = chksum(buf);
119
        if (hash->hash != chksum(buf)) {
120
                struct timeval now;
121
                gettimeofday(&now, NULL);
122
                DBG("%s: hash table: 0x%020" PRIx64 " at %012lu.%06lu, "
123
                    "from disk: 0x%020" PRIx64 " at %012lu.%06lu\n",
124
                    type, hash->hash, hash->time.tv_sec,
125
                    hash->time.tv_usec, sum, now.tv_sec, now.tv_usec);
126
        }
127
}
128

    
129
static inline void
130
insert_hash(struct tfilter *filter, uint64_t sec, char *buf)
131
{
132
        struct dhash *hash;
133

    
134
        hash = filter->dhash + sec;
135
        hash->hash = chksum(buf);
136
        gettimeofday(&hash->time, NULL);
137
}
138

    
139
static void
140
check_sector(struct tfilter *filter, int type, int rw, uint64_t sec, char *buf)
141
{
142
        if (sec >= filter->secs)
143
                return;
144

    
145
        if (rw) {
146
                if (type == PRE_CHECK)
147
                        insert_hash(filter, sec, buf);
148
                else
149
                        check_hash(filter, sec, buf, WRITE_INTEGRITY);
150
        } else if (type == POST_CHECK) {
151
                check_hash(filter, sec, buf, READ_INTEGRITY);
152
                insert_hash(filter, sec, buf);
153
        }
154
}
155

    
156
static void
157
check_data(struct tfilter *filter, int type, struct iocb *io)
158
{
159
        int rw;
160
        uint64_t i;
161

    
162
        rw = (io->aio_lio_opcode == IO_CMD_PWRITE);
163

    
164
        for (i = 0; i < io->u.c.nbytes; i += 512) {
165
                char *buf    = io->u.c.buf + i;
166
                uint64_t sec = (io->u.c.offset + i) >> 9;
167
                check_sector(filter, type, rw, sec, buf);
168
        }
169
}
170

    
171
struct tfilter *
172
tapdisk_init_tfilter(int mode, int iocbs, uint64_t secs)
173
{
174
        int i;
175
        struct tfilter *filter = NULL;
176

    
177
        if (!mode)
178
                return NULL;
179

    
180
        filter = calloc(1, sizeof(struct tfilter));
181
        if (!filter)
182
                goto fail;
183

    
184
        filter->mode  = mode;
185
        filter->secs  = secs;
186
        filter->iocbs = iocbs;
187

    
188
        if (filter->mode & TD_INJECT_FAULTS) {
189
                filter->fiocbs = calloc(iocbs, sizeof(struct fiocb));
190
                filter->flist  = calloc(iocbs, sizeof(struct fiocb *));
191
                if (!filter->fiocbs || !filter->flist)
192
                        filter->mode &= ~TD_INJECT_FAULTS;
193
                else {
194
                        srand(RSEED);
195
                        filter->ffree = iocbs;
196
                        for (i = 0; i < iocbs; i++)
197
                                filter->flist[i] = filter->fiocbs + i;
198
                }
199
        }
200

    
201
        if (filter->mode & TD_CHECK_INTEGRITY) {
202
                filter->dhash = calloc(secs, sizeof(struct dhash));
203
                if (!filter->dhash)
204
                        filter->mode &= ~TD_CHECK_INTEGRITY;
205
        }
206

    
207
        syslog(LOG_WARNING, "WARNING: "
208
               "FILTERING IN MODE 0x%04x\n", filter->mode);
209

    
210
        return filter;
211

    
212
 fail:
213
        tapdisk_free_tfilter(filter);
214
        return NULL;
215
}
216

    
217
void
218
tapdisk_free_tfilter(struct tfilter *filter)
219
{
220
        if (!filter)
221
                return;
222

    
223
        free(filter->dhash);
224
        free(filter->flist);
225
        free(filter->fiocbs);
226
        free(filter);
227
}
228

    
229
void
230
tapdisk_filter_iocbs(struct tfilter *filter, struct iocb **iocbs, int num)
231
{
232
        int i;
233

    
234
        if (!filter)
235
                return;
236

    
237
        for (i = 0; i < num; i++) {
238
                struct iocb *io = iocbs[i];
239

    
240
                if (filter->mode & TD_INJECT_FAULTS) {
241
                        if ((random() % 100) <= TD_FAULT_RATE) {
242
                                inject_fault(filter, io);
243
                                continue;
244
                        }
245
                }
246

    
247
                if (filter->mode & TD_CHECK_INTEGRITY)
248
                        check_data(filter, PRE_CHECK, io);
249
        }
250
}
251

    
252
void
253
tapdisk_filter_events(struct tfilter *filter, struct io_event *events, int num)
254
{
255
        int i;
256

    
257
        if (!filter)
258
                return;
259

    
260
        for (i = 0; i < num; i++) {
261
                struct iocb *io = events[i].obj;
262

    
263
                if (filter->mode & TD_INJECT_FAULTS) {
264
                        if (fault_injected(filter, io)) {
265
                                recover_fault(filter, io);
266
                                continue;
267
                        }
268
                }
269

    
270
                if (filter->mode & TD_CHECK_INTEGRITY)
271
                        check_data(filter, POST_CHECK, io);
272
        }
273
}