1
2
3
4
5
6
7
8
9
10#include <linux/badblocks.h>
11#include <linux/seqlock.h>
12#include <linux/device.h>
13#include <linux/kernel.h>
14#include <linux/module.h>
15#include <linux/stddef.h>
16#include <linux/types.h>
17#include <linux/slab.h>
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53int badblocks_check(struct badblocks *bb, sector_t s, int sectors,
54 sector_t *first_bad, int *bad_sectors)
55{
56 int hi;
57 int lo;
58 u64 *p = bb->page;
59 int rv;
60 sector_t target = s + sectors;
61 unsigned seq;
62
63 if (bb->shift > 0) {
64
65 s >>= bb->shift;
66 target += (1<<bb->shift) - 1;
67 target >>= bb->shift;
68 sectors = target - s;
69 }
70
71
72retry:
73 seq = read_seqbegin(&bb->lock);
74 lo = 0;
75 rv = 0;
76 hi = bb->count;
77
78
79
80
81
82
83
84
85
86 while (hi - lo > 1) {
87 int mid = (lo + hi) / 2;
88 sector_t a = BB_OFFSET(p[mid]);
89
90 if (a < target)
91
92
93
94 lo = mid;
95 else
96
97 hi = mid;
98 }
99
100 if (hi > lo) {
101
102
103
104 while (lo >= 0 &&
105 BB_OFFSET(p[lo]) + BB_LEN(p[lo]) > s) {
106 if (BB_OFFSET(p[lo]) < target) {
107
108
109
110 if (rv != -1 && BB_ACK(p[lo]))
111 rv = 1;
112 else
113 rv = -1;
114 *first_bad = BB_OFFSET(p[lo]);
115 *bad_sectors = BB_LEN(p[lo]);
116 }
117 lo--;
118 }
119 }
120
121 if (read_seqretry(&bb->lock, seq))
122 goto retry;
123
124 return rv;
125}
126EXPORT_SYMBOL_GPL(badblocks_check);
127
128static void badblocks_update_acked(struct badblocks *bb)
129{
130 u64 *p = bb->page;
131 int i;
132 bool unacked = false;
133
134 if (!bb->unacked_exist)
135 return;
136
137 for (i = 0; i < bb->count ; i++) {
138 if (!BB_ACK(p[i])) {
139 unacked = true;
140 break;
141 }
142 }
143
144 if (!unacked)
145 bb->unacked_exist = 0;
146}
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163int badblocks_set(struct badblocks *bb, sector_t s, int sectors,
164 int acknowledged)
165{
166 u64 *p;
167 int lo, hi;
168 int rv = 0;
169 unsigned long flags;
170
171 if (bb->shift < 0)
172
173 return 1;
174
175 if (bb->shift) {
176
177 sector_t next = s + sectors;
178
179 s >>= bb->shift;
180 next += (1<<bb->shift) - 1;
181 next >>= bb->shift;
182 sectors = next - s;
183 }
184
185 write_seqlock_irqsave(&bb->lock, flags);
186
187 p = bb->page;
188 lo = 0;
189 hi = bb->count;
190
191 while (hi - lo > 1) {
192 int mid = (lo + hi) / 2;
193 sector_t a = BB_OFFSET(p[mid]);
194
195 if (a <= s)
196 lo = mid;
197 else
198 hi = mid;
199 }
200 if (hi > lo && BB_OFFSET(p[lo]) > s)
201 hi = lo;
202
203 if (hi > lo) {
204
205
206
207 sector_t a = BB_OFFSET(p[lo]);
208 sector_t e = a + BB_LEN(p[lo]);
209 int ack = BB_ACK(p[lo]);
210
211 if (e >= s) {
212
213 if (s == a && s + sectors >= e)
214
215 ack = acknowledged;
216 else
217 ack = ack && acknowledged;
218
219 if (e < s + sectors)
220 e = s + sectors;
221 if (e - a <= BB_MAX_LEN) {
222 p[lo] = BB_MAKE(a, e-a, ack);
223 s = e;
224 } else {
225
226
227
228 if (BB_LEN(p[lo]) != BB_MAX_LEN)
229 p[lo] = BB_MAKE(a, BB_MAX_LEN, ack);
230 s = a + BB_MAX_LEN;
231 }
232 sectors = e - s;
233 }
234 }
235 if (sectors && hi < bb->count) {
236
237
238
239 sector_t a = BB_OFFSET(p[hi]);
240 sector_t e = a + BB_LEN(p[hi]);
241 int ack = BB_ACK(p[hi]);
242
243 if (a <= s + sectors) {
244
245 if (e <= s + sectors) {
246
247 e = s + sectors;
248 ack = acknowledged;
249 } else
250 ack = ack && acknowledged;
251
252 a = s;
253 if (e - a <= BB_MAX_LEN) {
254 p[hi] = BB_MAKE(a, e-a, ack);
255 s = e;
256 } else {
257 p[hi] = BB_MAKE(a, BB_MAX_LEN, ack);
258 s = a + BB_MAX_LEN;
259 }
260 sectors = e - s;
261 lo = hi;
262 hi++;
263 }
264 }
265 if (sectors == 0 && hi < bb->count) {
266
267
268 sector_t a = BB_OFFSET(p[hi]);
269 int lolen = BB_LEN(p[lo]);
270 int hilen = BB_LEN(p[hi]);
271 int newlen = lolen + hilen - (s - a);
272
273 if (s >= a && newlen < BB_MAX_LEN) {
274
275 int ack = BB_ACK(p[lo]) && BB_ACK(p[hi]);
276
277 p[lo] = BB_MAKE(BB_OFFSET(p[lo]), newlen, ack);
278 memmove(p + hi, p + hi + 1,
279 (bb->count - hi - 1) * 8);
280 bb->count--;
281 }
282 }
283 while (sectors) {
284
285
286
287 if (bb->count >= MAX_BADBLOCKS) {
288
289 rv = 1;
290 break;
291 } else {
292 int this_sectors = sectors;
293
294 memmove(p + hi + 1, p + hi,
295 (bb->count - hi) * 8);
296 bb->count++;
297
298 if (this_sectors > BB_MAX_LEN)
299 this_sectors = BB_MAX_LEN;
300 p[hi] = BB_MAKE(s, this_sectors, acknowledged);
301 sectors -= this_sectors;
302 s += this_sectors;
303 }
304 }
305
306 bb->changed = 1;
307 if (!acknowledged)
308 bb->unacked_exist = 1;
309 else
310 badblocks_update_acked(bb);
311 write_sequnlock_irqrestore(&bb->lock, flags);
312
313 return rv;
314}
315EXPORT_SYMBOL_GPL(badblocks_set);
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331int badblocks_clear(struct badblocks *bb, sector_t s, int sectors)
332{
333 u64 *p;
334 int lo, hi;
335 sector_t target = s + sectors;
336 int rv = 0;
337
338 if (bb->shift > 0) {
339
340
341
342
343
344
345 s += (1<<bb->shift) - 1;
346 s >>= bb->shift;
347 target >>= bb->shift;
348 sectors = target - s;
349 }
350
351 write_seqlock_irq(&bb->lock);
352
353 p = bb->page;
354 lo = 0;
355 hi = bb->count;
356
357 while (hi - lo > 1) {
358 int mid = (lo + hi) / 2;
359 sector_t a = BB_OFFSET(p[mid]);
360
361 if (a < target)
362 lo = mid;
363 else
364 hi = mid;
365 }
366 if (hi > lo) {
367
368
369
370
371 if ((BB_OFFSET(p[lo]) + BB_LEN(p[lo]) > target) &&
372 (BB_OFFSET(p[lo]) < target)) {
373
374 int ack = BB_ACK(p[lo]);
375 sector_t a = BB_OFFSET(p[lo]);
376 sector_t end = a + BB_LEN(p[lo]);
377
378 if (a < s) {
379
380 if (bb->count >= MAX_BADBLOCKS) {
381 rv = -ENOSPC;
382 goto out;
383 }
384 memmove(p+lo+1, p+lo, (bb->count - lo) * 8);
385 bb->count++;
386 p[lo] = BB_MAKE(a, s-a, ack);
387 lo++;
388 }
389 p[lo] = BB_MAKE(target, end - target, ack);
390
391 hi = lo;
392 lo--;
393 }
394 while (lo >= 0 &&
395 (BB_OFFSET(p[lo]) + BB_LEN(p[lo]) > s) &&
396 (BB_OFFSET(p[lo]) < target)) {
397
398 if (BB_OFFSET(p[lo]) < s) {
399
400 int ack = BB_ACK(p[lo]);
401 sector_t start = BB_OFFSET(p[lo]);
402
403 p[lo] = BB_MAKE(start, s - start, ack);
404
405 break;
406 }
407 lo--;
408 }
409
410
411
412 if (hi - lo > 1) {
413 memmove(p+lo+1, p+hi, (bb->count - hi) * 8);
414 bb->count -= (hi - lo - 1);
415 }
416 }
417
418 badblocks_update_acked(bb);
419 bb->changed = 1;
420out:
421 write_sequnlock_irq(&bb->lock);
422 return rv;
423}
424EXPORT_SYMBOL_GPL(badblocks_clear);
425
426
427
428
429
430
431
432
433void ack_all_badblocks(struct badblocks *bb)
434{
435 if (bb->page == NULL || bb->changed)
436
437 return;
438 write_seqlock_irq(&bb->lock);
439
440 if (bb->changed == 0 && bb->unacked_exist) {
441 u64 *p = bb->page;
442 int i;
443
444 for (i = 0; i < bb->count ; i++) {
445 if (!BB_ACK(p[i])) {
446 sector_t start = BB_OFFSET(p[i]);
447 int len = BB_LEN(p[i]);
448
449 p[i] = BB_MAKE(start, len, 1);
450 }
451 }
452 bb->unacked_exist = 0;
453 }
454 write_sequnlock_irq(&bb->lock);
455}
456EXPORT_SYMBOL_GPL(ack_all_badblocks);
457
458
459
460
461
462
463
464
465
466
467ssize_t badblocks_show(struct badblocks *bb, char *page, int unack)
468{
469 size_t len;
470 int i;
471 u64 *p = bb->page;
472 unsigned seq;
473
474 if (bb->shift < 0)
475 return 0;
476
477retry:
478 seq = read_seqbegin(&bb->lock);
479
480 len = 0;
481 i = 0;
482
483 while (len < PAGE_SIZE && i < bb->count) {
484 sector_t s = BB_OFFSET(p[i]);
485 unsigned int length = BB_LEN(p[i]);
486 int ack = BB_ACK(p[i]);
487
488 i++;
489
490 if (unack && ack)
491 continue;
492
493 len += snprintf(page+len, PAGE_SIZE-len, "%llu %u\n",
494 (unsigned long long)s << bb->shift,
495 length << bb->shift);
496 }
497 if (unack && len == 0)
498 bb->unacked_exist = 0;
499
500 if (read_seqretry(&bb->lock, seq))
501 goto retry;
502
503 return len;
504}
505EXPORT_SYMBOL_GPL(badblocks_show);
506
507
508
509
510
511
512
513
514
515
516
517ssize_t badblocks_store(struct badblocks *bb, const char *page, size_t len,
518 int unack)
519{
520 unsigned long long sector;
521 int length;
522 char newline;
523
524 switch (sscanf(page, "%llu %d%c", §or, &length, &newline)) {
525 case 3:
526 if (newline != '\n')
527 return -EINVAL;
528
529 case 2:
530 if (length <= 0)
531 return -EINVAL;
532 break;
533 default:
534 return -EINVAL;
535 }
536
537 if (badblocks_set(bb, sector, length, !unack))
538 return -ENOSPC;
539 else
540 return len;
541}
542EXPORT_SYMBOL_GPL(badblocks_store);
543
544static int __badblocks_init(struct device *dev, struct badblocks *bb,
545 int enable)
546{
547 bb->dev = dev;
548 bb->count = 0;
549 if (enable)
550 bb->shift = 0;
551 else
552 bb->shift = -1;
553 if (dev)
554 bb->page = devm_kzalloc(dev, PAGE_SIZE, GFP_KERNEL);
555 else
556 bb->page = kzalloc(PAGE_SIZE, GFP_KERNEL);
557 if (!bb->page) {
558 bb->shift = -1;
559 return -ENOMEM;
560 }
561 seqlock_init(&bb->lock);
562
563 return 0;
564}
565
566
567
568
569
570
571
572
573
574
575int badblocks_init(struct badblocks *bb, int enable)
576{
577 return __badblocks_init(NULL, bb, enable);
578}
579EXPORT_SYMBOL_GPL(badblocks_init);
580
581int devm_init_badblocks(struct device *dev, struct badblocks *bb)
582{
583 if (!bb)
584 return -EINVAL;
585 return __badblocks_init(dev, bb, 1);
586}
587EXPORT_SYMBOL_GPL(devm_init_badblocks);
588
589
590
591
592
593void badblocks_exit(struct badblocks *bb)
594{
595 if (!bb)
596 return;
597 if (bb->dev)
598 devm_kfree(bb->dev, bb->page);
599 else
600 kfree(bb->page);
601 bb->page = NULL;
602}
603EXPORT_SYMBOL_GPL(badblocks_exit);
604