1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (C) 2001-2002 Sistina Software (UK) Limited. |
4 | * Copyright (C) 2006-2008 Red Hat GmbH |
5 | * |
6 | * This file is released under the GPL. |
7 | */ |
8 | |
9 | #include "dm-exception-store.h" |
10 | |
11 | #include <linux/mm.h> |
12 | #include <linux/pagemap.h> |
13 | #include <linux/vmalloc.h> |
14 | #include <linux/export.h> |
15 | #include <linux/slab.h> |
16 | #include <linux/dm-io.h> |
17 | |
18 | #define DM_MSG_PREFIX "transient snapshot" |
19 | |
20 | /* |
21 | *--------------------------------------------------------------- |
22 | * Implementation of the store for non-persistent snapshots. |
23 | *--------------------------------------------------------------- |
24 | */ |
25 | struct transient_c { |
26 | sector_t next_free; |
27 | }; |
28 | |
29 | static void transient_dtr(struct dm_exception_store *store) |
30 | { |
31 | kfree(objp: store->context); |
32 | } |
33 | |
34 | static int transient_read_metadata(struct dm_exception_store *store, |
35 | int (*callback)(void *callback_context, |
36 | chunk_t old, chunk_t new), |
37 | void *callback_context) |
38 | { |
39 | return 0; |
40 | } |
41 | |
42 | static int transient_prepare_exception(struct dm_exception_store *store, |
43 | struct dm_exception *e) |
44 | { |
45 | struct transient_c *tc = store->context; |
46 | sector_t size = get_dev_size(bdev: dm_snap_cow(snap: store->snap)->bdev); |
47 | |
48 | if (size < (tc->next_free + store->chunk_size)) |
49 | return -1; |
50 | |
51 | e->new_chunk = sector_to_chunk(store, sector: tc->next_free); |
52 | tc->next_free += store->chunk_size; |
53 | |
54 | return 0; |
55 | } |
56 | |
57 | static void transient_commit_exception(struct dm_exception_store *store, |
58 | struct dm_exception *e, int valid, |
59 | void (*callback)(void *, int success), |
60 | void *callback_context) |
61 | { |
62 | /* Just succeed */ |
63 | callback(callback_context, valid); |
64 | } |
65 | |
66 | static void transient_usage(struct dm_exception_store *store, |
67 | sector_t *total_sectors, |
68 | sector_t *sectors_allocated, |
69 | sector_t *metadata_sectors) |
70 | { |
71 | *sectors_allocated = ((struct transient_c *) store->context)->next_free; |
72 | *total_sectors = get_dev_size(bdev: dm_snap_cow(snap: store->snap)->bdev); |
73 | *metadata_sectors = 0; |
74 | } |
75 | |
76 | static int transient_ctr(struct dm_exception_store *store, char *options) |
77 | { |
78 | struct transient_c *tc; |
79 | |
80 | tc = kmalloc(size: sizeof(struct transient_c), GFP_KERNEL); |
81 | if (!tc) |
82 | return -ENOMEM; |
83 | |
84 | tc->next_free = 0; |
85 | store->context = tc; |
86 | |
87 | return 0; |
88 | } |
89 | |
90 | static unsigned int transient_status(struct dm_exception_store *store, |
91 | status_type_t status, char *result, |
92 | unsigned int maxlen) |
93 | { |
94 | unsigned int sz = 0; |
95 | |
96 | switch (status) { |
97 | case STATUSTYPE_INFO: |
98 | break; |
99 | case STATUSTYPE_TABLE: |
100 | DMEMIT(" N %llu" , (unsigned long long)store->chunk_size); |
101 | break; |
102 | case STATUSTYPE_IMA: |
103 | *result = '\0'; |
104 | break; |
105 | } |
106 | |
107 | return sz; |
108 | } |
109 | |
110 | static struct dm_exception_store_type _transient_type = { |
111 | .name = "transient" , |
112 | .module = THIS_MODULE, |
113 | .ctr = transient_ctr, |
114 | .dtr = transient_dtr, |
115 | .read_metadata = transient_read_metadata, |
116 | .prepare_exception = transient_prepare_exception, |
117 | .commit_exception = transient_commit_exception, |
118 | .usage = transient_usage, |
119 | .status = transient_status, |
120 | }; |
121 | |
122 | static struct dm_exception_store_type _transient_compat_type = { |
123 | .name = "N" , |
124 | .module = THIS_MODULE, |
125 | .ctr = transient_ctr, |
126 | .dtr = transient_dtr, |
127 | .read_metadata = transient_read_metadata, |
128 | .prepare_exception = transient_prepare_exception, |
129 | .commit_exception = transient_commit_exception, |
130 | .usage = transient_usage, |
131 | .status = transient_status, |
132 | }; |
133 | |
134 | int dm_transient_snapshot_init(void) |
135 | { |
136 | int r; |
137 | |
138 | r = dm_exception_store_type_register(type: &_transient_type); |
139 | if (r) { |
140 | DMWARN("Unable to register transient exception store type" ); |
141 | return r; |
142 | } |
143 | |
144 | r = dm_exception_store_type_register(type: &_transient_compat_type); |
145 | if (r) { |
146 | DMWARN("Unable to register old-style transient exception store type" ); |
147 | dm_exception_store_type_unregister(type: &_transient_type); |
148 | return r; |
149 | } |
150 | |
151 | return r; |
152 | } |
153 | |
154 | void dm_transient_snapshot_exit(void) |
155 | { |
156 | dm_exception_store_type_unregister(type: &_transient_type); |
157 | dm_exception_store_type_unregister(type: &_transient_compat_type); |
158 | } |
159 | |