xref: /haiku/src/add-ons/kernel/drivers/disk/virtual/ram_disk/ram_disk.cpp (revision b5efad4eae1a0754ce3e4eb87458279e81f61f61)
1 /*
2  * Copyright 2010-2013, Ingo Weinhold, ingo_weinhold@gmx.de.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include <file_systems/ram_disk/ram_disk.h>
8 
9 #include <ctype.h>
10 #include <errno.h>
11 #include <fcntl.h>
12 #include <stdio.h>
13 #include <string.h>
14 #include <unistd.h>
15 
16 #include <algorithm>
17 
18 #include <device_manager.h>
19 #include <Drivers.h>
20 
21 #include <AutoDeleter.h>
22 #include <util/AutoLock.h>
23 #include <util/DoublyLinkedList.h>
24 
25 #include <fs/KPath.h>
26 #include <lock.h>
27 #include <util/fs_trim_support.h>
28 #include <vm/vm.h>
29 #include <vm/VMCache.h>
30 #include <vm/vm_page.h>
31 
32 #include "dma_resources.h"
33 #include "io_requests.h"
34 #include "IOSchedulerSimple.h"
35 
36 
37 //#define TRACE_RAM_DISK
38 #ifdef TRACE_RAM_DISK
39 #	define TRACE(x...)	dprintf(x)
40 #else
41 #	define TRACE(x...) do {} while (false)
42 #endif
43 
44 
45 static const unsigned char kRamdiskIcon[] = {
46 	0x6e, 0x63, 0x69, 0x66, 0x0e, 0x03, 0x01, 0x00, 0x00, 0x02, 0x00, 0x16,
47 	0x02, 0x3c, 0xc7, 0xee, 0x38, 0x9b, 0xc0, 0xba, 0x16, 0x57, 0x3e, 0x39,
48 	0xb0, 0x49, 0x77, 0xc8, 0x42, 0xad, 0xc7, 0x00, 0xff, 0xff, 0xd3, 0x02,
49 	0x00, 0x06, 0x02, 0x3c, 0x96, 0x32, 0x3a, 0x4d, 0x3f, 0xba, 0xfc, 0x01,
50 	0x3d, 0x5a, 0x97, 0x4b, 0x57, 0xa5, 0x49, 0x84, 0x4d, 0x00, 0x47, 0x47,
51 	0x47, 0xff, 0xa5, 0xa0, 0xa0, 0x02, 0x00, 0x16, 0x02, 0xbc, 0x59, 0x2f,
52 	0xbb, 0x29, 0xa7, 0x3c, 0x0c, 0xe4, 0xbd, 0x0b, 0x7c, 0x48, 0x92, 0xc0,
53 	0x4b, 0x79, 0x66, 0x00, 0x7d, 0xff, 0xd4, 0x02, 0x00, 0x06, 0x02, 0x38,
54 	0xdb, 0xb4, 0x39, 0x97, 0x33, 0xbc, 0x4a, 0x33, 0x3b, 0xa5, 0x42, 0x48,
55 	0x6e, 0x66, 0x49, 0xee, 0x7b, 0x00, 0x59, 0x67, 0x56, 0xff, 0xeb, 0xb2,
56 	0xb2, 0x03, 0xa7, 0xff, 0x00, 0x03, 0xff, 0x00, 0x00, 0x04, 0x01, 0x80,
57 	0x03, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x6a, 0x05, 0x33, 0x02,
58 	0x00, 0x06, 0x02, 0x3a, 0x5d, 0x2c, 0x39, 0xf8, 0xb1, 0xb9, 0xdb, 0xf1,
59 	0x3a, 0x4c, 0x0f, 0x48, 0xae, 0xea, 0x4a, 0xc0, 0x91, 0x00, 0x74, 0x74,
60 	0x74, 0xff, 0x3e, 0x3d, 0x3d, 0x02, 0x00, 0x16, 0x02, 0x38, 0x22, 0x1b,
61 	0x3b, 0x11, 0x73, 0xbc, 0x5e, 0xb5, 0x39, 0x4b, 0xaa, 0x4a, 0x47, 0xf1,
62 	0x49, 0xc2, 0x1d, 0x00, 0xb0, 0xff, 0x83, 0x02, 0x00, 0x16, 0x03, 0x36,
63 	0xed, 0xe9, 0x36, 0xb9, 0x49, 0xba, 0x0a, 0xf6, 0x3a, 0x32, 0x6f, 0x4a,
64 	0x79, 0xef, 0x4b, 0x03, 0xe7, 0x00, 0x5a, 0x38, 0xdc, 0xff, 0x7e, 0x0d,
65 	0x0a, 0x06, 0x22, 0x3c, 0x22, 0x49, 0x44, 0x5b, 0x5a, 0x3e, 0x5a, 0x31,
66 	0x39, 0x25, 0x0a, 0x04, 0x22, 0x3c, 0x44, 0x4b, 0x5a, 0x31, 0x39, 0x25,
67 	0x0a, 0x04, 0x44, 0x4b, 0x44, 0x5b, 0x5a, 0x3e, 0x5a, 0x31, 0x0a, 0x04,
68 	0x22, 0x3c, 0x22, 0x49, 0x44, 0x5b, 0x44, 0x4b, 0x08, 0x02, 0x27, 0x43,
69 	0xb8, 0x14, 0xc1, 0xf1, 0x08, 0x02, 0x26, 0x43, 0x29, 0x44, 0x0a, 0x05,
70 	0x44, 0x5d, 0x49, 0x5d, 0x60, 0x3e, 0x5a, 0x3b, 0x5b, 0x3f, 0x0a, 0x04,
71 	0x3c, 0x5a, 0x5a, 0x3c, 0x5a, 0x36, 0x3c, 0x52, 0x0a, 0x04, 0x24, 0x4e,
72 	0x3c, 0x5a, 0x3c, 0x52, 0x24, 0x48, 0x06, 0x07, 0xaa, 0x3f, 0x42, 0x2e,
73 	0x24, 0x48, 0x3c, 0x52, 0x5a, 0x36, 0x51, 0x33, 0x51, 0x33, 0x50, 0x34,
74 	0x4b, 0x33, 0x4d, 0x34, 0x49, 0x32, 0x49, 0x30, 0x48, 0x31, 0x49, 0x30,
75 	0x06, 0x08, 0xfa, 0xfa, 0x42, 0x50, 0x3e, 0x54, 0x40, 0x55, 0x3f, 0xc7,
76 	0xeb, 0x41, 0xc8, 0x51, 0x42, 0xc9, 0x4f, 0x42, 0xc8, 0xda, 0x42, 0xca,
77 	0x41, 0xc0, 0xf1, 0x5d, 0x45, 0xca, 0x81, 0x46, 0xc7, 0xb7, 0x46, 0xc8,
78 	0xa9, 0x46, 0xc7, 0x42, 0x44, 0x51, 0x45, 0xc6, 0xb9, 0x43, 0xc6, 0x53,
79 	0x0a, 0x07, 0x3c, 0x5c, 0x40, 0x5c, 0x42, 0x5e, 0x48, 0x5e, 0x4a, 0x5c,
80 	0x46, 0x5a, 0x45, 0x4b, 0x06, 0x09, 0x9a, 0xf6, 0x03, 0x42, 0x2e, 0x24,
81 	0x48, 0x4e, 0x3c, 0x5a, 0x5a, 0x3c, 0x36, 0x51, 0x33, 0x51, 0x33, 0x50,
82 	0x34, 0x4b, 0x33, 0x4d, 0x34, 0x49, 0x32, 0x49, 0x30, 0x48, 0x31, 0x49,
83 	0x30, 0x18, 0x0a, 0x07, 0x01, 0x06, 0x00, 0x0a, 0x00, 0x01, 0x00, 0x10,
84 	0x01, 0x17, 0x84, 0x00, 0x04, 0x0a, 0x01, 0x01, 0x01, 0x00, 0x0a, 0x02,
85 	0x01, 0x02, 0x00, 0x0a, 0x03, 0x01, 0x03, 0x00, 0x0a, 0x04, 0x01, 0x04,
86 	0x10, 0x01, 0x17, 0x85, 0x20, 0x04, 0x0a, 0x06, 0x01, 0x05, 0x30, 0x24,
87 	0xb3, 0x99, 0x01, 0x17, 0x82, 0x00, 0x04, 0x0a, 0x05, 0x01, 0x05, 0x30,
88 	0x20, 0xb2, 0xe6, 0x01, 0x17, 0x82, 0x00, 0x04, 0x0a, 0x09, 0x01, 0x0b,
89 	0x02, 0x3e, 0x9b, 0x12, 0xb5, 0xf9, 0x99, 0x36, 0x19, 0x10, 0x3e, 0xc0,
90 	0x21, 0x48, 0xed, 0x4d, 0xc8, 0x5a, 0x02, 0x0a, 0x09, 0x01, 0x0b, 0x02,
91 	0x3e, 0x9b, 0x12, 0xb5, 0xf9, 0x99, 0x36, 0x19, 0x10, 0x3e, 0xc0, 0x21,
92 	0x48, 0x4c, 0xd4, 0xc7, 0x9c, 0x11, 0x0a, 0x09, 0x01, 0x0b, 0x02, 0x3e,
93 	0x9b, 0x12, 0xb5, 0xf9, 0x99, 0x36, 0x19, 0x10, 0x3e, 0xc0, 0x21, 0x47,
94 	0x5c, 0xe7, 0xc6, 0x2c, 0x1a, 0x0a, 0x09, 0x01, 0x0b, 0x02, 0x3e, 0x9b,
95 	0x12, 0xb5, 0xf9, 0x99, 0x36, 0x19, 0x10, 0x3e, 0xc0, 0x21, 0x46, 0x1b,
96 	0xf5, 0xc4, 0x28, 0x4e, 0x0a, 0x08, 0x01, 0x0c, 0x12, 0x3e, 0xc0, 0x21,
97 	0xb6, 0x19, 0x10, 0x36, 0x19, 0x10, 0x3e, 0xc0, 0x21, 0x45, 0xb6, 0x34,
98 	0xc4, 0x22, 0x1f, 0x01, 0x17, 0x84, 0x00, 0x04, 0x0a, 0x0a, 0x01, 0x07,
99 	0x02, 0x3e, 0xc0, 0x21, 0xb6, 0x19, 0x10, 0x36, 0x19, 0x10, 0x3e, 0xc0,
100 	0x21, 0x45, 0xb6, 0x34, 0xc4, 0x22, 0x1f, 0x0a, 0x0b, 0x01, 0x08, 0x02,
101 	0x3e, 0xc0, 0x21, 0xb6, 0x19, 0x10, 0x36, 0x19, 0x10, 0x3e, 0xc0, 0x21,
102 	0x45, 0xb6, 0x34, 0xc4, 0x22, 0x1f, 0x0a, 0x0c, 0x01, 0x09, 0x02, 0x3e,
103 	0xc0, 0x21, 0xb6, 0x19, 0x10, 0x36, 0x19, 0x10, 0x3e, 0xc0, 0x21, 0x45,
104 	0xb6, 0x34, 0xc4, 0x22, 0x1f, 0x0a, 0x08, 0x01, 0x0a, 0x12, 0x3e, 0x98,
105 	0xfd, 0xb5, 0xf6, 0x6c, 0x35, 0xc9, 0x3d, 0x3e, 0x7b, 0x5e, 0x48, 0xf2,
106 	0x4e, 0xc7, 0xee, 0x3f, 0x01, 0x17, 0x84, 0x22, 0x04, 0x0a, 0x0d, 0x01,
107 	0x0a, 0x02, 0x3e, 0x98, 0xfd, 0xb5, 0xf6, 0x6c, 0x35, 0xc9, 0x3d, 0x3e,
108 	0x7b, 0x5e, 0x48, 0xf2, 0x4e, 0xc7, 0xee, 0x3f, 0x0a, 0x08, 0x01, 0x0a,
109 	0x12, 0x3e, 0x98, 0xfd, 0xb5, 0xf6, 0x6c, 0x35, 0xc9, 0x3d, 0x3e, 0x7b,
110 	0x5e, 0x48, 0x53, 0xa1, 0xc6, 0xa0, 0xb6, 0x01, 0x17, 0x84, 0x22, 0x04,
111 	0x0a, 0x0d, 0x01, 0x0a, 0x02, 0x3e, 0x98, 0xfd, 0xb5, 0xf6, 0x6c, 0x35,
112 	0xc9, 0x3d, 0x3e, 0x7b, 0x5e, 0x48, 0x53, 0xa1, 0xc6, 0xa0, 0xb6, 0x0a,
113 	0x08, 0x01, 0x0a, 0x12, 0x3e, 0x98, 0xfd, 0xb5, 0xf6, 0x6c, 0x35, 0xc9,
114 	0x3d, 0x3e, 0x7b, 0x5e, 0x47, 0x69, 0xe9, 0xc4, 0xa6, 0x5a, 0x01, 0x17,
115 	0x84, 0x22, 0x04, 0x0a, 0x0d, 0x01, 0x0a, 0x02, 0x3e, 0x98, 0xfd, 0xb5,
116 	0xf6, 0x6c, 0x35, 0xc9, 0x3d, 0x3e, 0x7b, 0x5e, 0x47, 0x69, 0xe9, 0xc4,
117 	0xa6, 0x5a, 0x0a, 0x08, 0x01, 0x0a, 0x12, 0x3e, 0x98, 0xfd, 0xb5, 0xf6,
118 	0x6c, 0x35, 0xc9, 0x3d, 0x3e, 0x7b, 0x5e, 0x46, 0x2c, 0x90, 0xb8, 0xd1,
119 	0xff, 0x01, 0x17, 0x84, 0x22, 0x04, 0x0a, 0x0d, 0x01, 0x0a, 0x02, 0x3e,
120 	0x98, 0xfd, 0xb5, 0xf6, 0x6c, 0x35, 0xc9, 0x3d, 0x3e, 0x7b, 0x5e, 0x46,
121 	0x2c, 0x90, 0xb8, 0xd1, 0xff
122 };
123 
124 
125 // parameters for the DMA resource
126 static const uint32 kDMAResourceBufferCount			= 16;
127 static const uint32 kDMAResourceBounceBufferCount	= 16;
128 
129 static const char* const kDriverModuleName
130 	= "drivers/disk/virtual/ram_disk/driver_v1";
131 static const char* const kControlDeviceModuleName
132 	= "drivers/disk/virtual/ram_disk/control/device_v1";
133 static const char* const kRawDeviceModuleName
134 	= "drivers/disk/virtual/ram_disk/raw/device_v1";
135 
136 static const char* const kControlDeviceName = RAM_DISK_CONTROL_DEVICE_NAME;
137 static const char* const kRawDeviceBaseName = RAM_DISK_RAW_DEVICE_BASE_NAME;
138 
139 static const char* const kFilePathItem = "ram_disk/file_path";
140 static const char* const kDeviceSizeItem = "ram_disk/device_size";
141 static const char* const kDeviceIDItem = "ram_disk/id";
142 
143 
144 struct RawDevice;
145 typedef DoublyLinkedList<RawDevice> RawDeviceList;
146 
147 struct device_manager_info* sDeviceManager;
148 
149 static RawDeviceList sDeviceList;
150 static mutex sDeviceListLock = MUTEX_INITIALIZER("ram disk device list");
151 static uint64 sUsedRawDeviceIDs = 0;
152 
153 
154 static int32	allocate_raw_device_id();
155 static void		free_raw_device_id(int32 id);
156 
157 
158 struct Device {
159 	Device(device_node* node)
160 		:
161 		fNode(node)
162 	{
163 		mutex_init(&fLock, "ram disk device");
164 	}
165 
166 	virtual ~Device()
167 	{
168 		mutex_destroy(&fLock);
169 	}
170 
171 	bool Lock()		{ mutex_lock(&fLock); return true; }
172 	void Unlock()	{ mutex_unlock(&fLock); }
173 
174 	device_node* Node() const	{ return fNode; }
175 
176 	virtual status_t PublishDevice() = 0;
177 
178 protected:
179 	mutex			fLock;
180 	device_node*	fNode;
181 };
182 
183 
184 struct ControlDevice : Device {
185 	ControlDevice(device_node* node)
186 		:
187 		Device(node)
188 	{
189 	}
190 
191 	status_t Register(const char* filePath, uint64 deviceSize, int32& _id)
192 	{
193 		int32 id = allocate_raw_device_id();
194 		if (id < 0)
195 			return B_BUSY;
196 
197 		device_attr attrs[] = {
198 			{B_DEVICE_PRETTY_NAME, B_STRING_TYPE,
199 				{string: "RAM Disk Raw Device"}},
200 			{kDeviceSizeItem, B_UINT64_TYPE, {ui64: deviceSize}},
201 			{kDeviceIDItem, B_UINT32_TYPE, {ui32: (uint32)id}},
202 			{kFilePathItem, B_STRING_TYPE, {string: filePath}},
203 			{NULL}
204 		};
205 
206 		// If filePath is NULL, remove the attribute.
207 		if (filePath == NULL) {
208 			size_t count = sizeof(attrs) / sizeof(attrs[0]);
209 			memset(attrs + count - 2, 0, sizeof(attrs[0]));
210 		}
211 
212 		status_t error = sDeviceManager->register_node(
213 			sDeviceManager->get_parent_node(Node()), kDriverModuleName, attrs,
214 			NULL, NULL);
215 		if (error != B_OK) {
216 			free_raw_device_id(id);
217 			return error;
218 		}
219 
220 		_id = id;
221 		return B_OK;
222 	}
223 
224 	virtual status_t PublishDevice()
225 	{
226 		return sDeviceManager->publish_device(Node(), kControlDeviceName,
227 			kControlDeviceModuleName);
228 	}
229 };
230 
231 
232 struct RawDevice : Device, DoublyLinkedListLinkImpl<RawDevice> {
233 	RawDevice(device_node* node)
234 		:
235 		Device(node),
236 		fID(-1),
237 		fUnregistered(false),
238 		fDeviceSize(0),
239 		fDeviceName(NULL),
240 		fFilePath(NULL),
241 		fCache(NULL),
242 		fDMAResource(NULL),
243 		fIOScheduler(NULL)
244 	{
245 	}
246 
247 	virtual ~RawDevice()
248 	{
249 		if (fID >= 0) {
250 			MutexLocker locker(sDeviceListLock);
251 			sDeviceList.Remove(this);
252 		}
253 
254 		free(fDeviceName);
255 		free(fFilePath);
256 	}
257 
258 	int32 ID() const				{ return fID; }
259 	off_t DeviceSize() const		{ return fDeviceSize; }
260 	const char* DeviceName() const	{ return fDeviceName; }
261 
262 	bool IsUnregistered() const		{ return fUnregistered; }
263 
264 	void SetUnregistered(bool unregistered)
265 	{
266 		fUnregistered = unregistered;
267 	}
268 
269 	status_t Init(int32 id, const char* filePath, uint64 deviceSize)
270 	{
271 		fID = id;
272 		fFilePath = filePath != NULL ? strdup(filePath) : NULL;
273 		if (filePath != NULL && fFilePath == NULL)
274 			return B_NO_MEMORY;
275 
276 		fDeviceSize = (deviceSize + B_PAGE_SIZE - 1) / B_PAGE_SIZE
277 			* B_PAGE_SIZE;
278 
279 		if (fDeviceSize < B_PAGE_SIZE
280 			|| (uint64)fDeviceSize / B_PAGE_SIZE
281 				> vm_page_num_pages() * 2 / 3) {
282 			return B_BAD_VALUE;
283 		}
284 
285 		// construct our device path
286 		KPath path(kRawDeviceBaseName);
287 		char buffer[32];
288 		snprintf(buffer, sizeof(buffer), "%" B_PRId32 "/raw", fID);
289 
290 		status_t error = path.Append(buffer);
291 		if (error != B_OK)
292 			return error;
293 
294 		fDeviceName = path.DetachBuffer();
295 
296 		// insert into device list
297 		RawDevice* nextDevice = NULL;
298 		MutexLocker locker(sDeviceListLock);
299 		for (RawDeviceList::Iterator it = sDeviceList.GetIterator();
300 				(nextDevice = it.Next()) != NULL;) {
301 			if (nextDevice->ID() > fID)
302 				break;
303 		}
304 
305 		sDeviceList.InsertBefore(nextDevice, this);
306 
307 		return B_OK;
308 	}
309 
310 	status_t Prepare()
311 	{
312 		status_t error = VMCacheFactory::CreateAnonymousCache(fCache, false, 0,
313 			0, false, VM_PRIORITY_SYSTEM);
314 		if (error != B_OK) {
315 			Unprepare();
316 			return error;
317 		}
318 
319 		fCache->temporary = 1;
320 		fCache->virtual_end = fDeviceSize;
321 
322 		error = fCache->Commit(fDeviceSize, VM_PRIORITY_SYSTEM);
323 		if (error != B_OK) {
324 			Unprepare();
325 			return error;
326 		}
327 
328 		if (fFilePath != NULL) {
329 			error = _LoadFile();
330 			if (error != B_OK) {
331 				Unprepare();
332 				return error;
333 			}
334 		}
335 
336 		// no DMA restrictions
337 		const dma_restrictions restrictions = {};
338 
339 		fDMAResource = new(std::nothrow) DMAResource;
340 		if (fDMAResource == NULL) {
341 			Unprepare();
342 			return B_NO_MEMORY;
343 		}
344 
345 		error = fDMAResource->Init(restrictions, B_PAGE_SIZE,
346 			kDMAResourceBufferCount, kDMAResourceBounceBufferCount);
347 		if (error != B_OK) {
348 			Unprepare();
349 			return error;
350 		}
351 
352 		fIOScheduler = new(std::nothrow) IOSchedulerSimple(fDMAResource);
353 		if (fIOScheduler == NULL) {
354 			Unprepare();
355 			return B_NO_MEMORY;
356 		}
357 
358 		error = fIOScheduler->Init("ram disk device scheduler");
359 		if (error != B_OK) {
360 			Unprepare();
361 			return error;
362 		}
363 
364 		fIOScheduler->SetCallback(&_DoIOEntry, this);
365 
366 		return B_OK;
367 	}
368 
369 	void Unprepare()
370 	{
371 		delete fIOScheduler;
372 		fIOScheduler = NULL;
373 
374 		delete fDMAResource;
375 		fDMAResource = NULL;
376 
377 		if (fCache != NULL) {
378 			fCache->Lock();
379 			fCache->ReleaseRefAndUnlock();
380 			fCache = NULL;
381 		}
382 	}
383 
384 	void GetInfo(ram_disk_ioctl_info& _info) const
385 	{
386 		_info.id = fID;
387 		_info.size = fDeviceSize;
388 		memset(&_info.path, 0, sizeof(_info.path));
389 		if (fFilePath != NULL)
390 			strlcpy(_info.path, fFilePath, sizeof(_info.path));
391 	}
392 
393 	status_t Flush()
394 	{
395 		static const size_t kPageCountPerIteration = 1024;
396 		static const size_t kMaxGapSize = 15;
397 
398 		int fd = open(fFilePath, O_WRONLY);
399 		if (fd < 0)
400 			return errno;
401 		FileDescriptorCloser fdCloser(fd);
402 
403 		vm_page** pages = new(std::nothrow) vm_page*[kPageCountPerIteration];
404 		ArrayDeleter<vm_page*> pagesDeleter(pages);
405 
406 		uint8* buffer = (uint8*)malloc(kPageCountPerIteration * B_PAGE_SIZE);
407 		MemoryDeleter bufferDeleter(buffer);
408 
409 		if (pages == NULL || buffer == NULL)
410 			return B_NO_MEMORY;
411 
412 		// Iterate through all pages of the cache and write those back that have
413 		// been modified.
414 		AutoLocker<VMCache> locker(fCache);
415 
416 		status_t error = B_OK;
417 
418 		for (off_t offset = 0; offset < fDeviceSize;) {
419 			// find the first modified page at or after the current offset
420 			VMCachePagesTree::Iterator it
421 				= fCache->pages.GetIterator(offset / B_PAGE_SIZE, true, true);
422 			vm_page* firstModified;
423 			while ((firstModified = it.Next()) != NULL
424 				&& !firstModified->modified) {
425 			}
426 
427 			if (firstModified == NULL)
428 				break;
429 
430 			if (firstModified->busy) {
431 				fCache->WaitForPageEvents(firstModified, PAGE_EVENT_NOT_BUSY,
432 					true);
433 				continue;
434 			}
435 
436 			pages[0] = firstModified;
437 			page_num_t firstPageIndex = firstModified->cache_offset;
438 			offset = firstPageIndex * B_PAGE_SIZE;
439 
440 			// Collect more pages until the gap between two modified pages gets
441 			// too large or we hit the end of our array.
442 			size_t previousModifiedIndex = 0;
443 			size_t previousIndex = 0;
444 			while (vm_page* page = it.Next()) {
445 				page_num_t index = page->cache_offset - firstPageIndex;
446 				if (page->busy
447 					|| index >= kPageCountPerIteration
448 					|| index - previousModifiedIndex > kMaxGapSize) {
449 					break;
450 				}
451 
452 				pages[index] = page;
453 
454 				// clear page array gap since the previous page
455 				if (previousIndex + 1 < index) {
456 					memset(pages + previousIndex + 1, 0,
457 						(index - previousIndex - 1) * sizeof(vm_page*));
458 				}
459 
460 				previousIndex = index;
461 				if (page->modified)
462 					previousModifiedIndex = index;
463 			}
464 
465 			// mark all pages we want to write busy
466 			size_t pagesToWrite = previousModifiedIndex + 1;
467 			for (size_t i = 0; i < pagesToWrite; i++) {
468 				if (vm_page* page = pages[i]) {
469 					DEBUG_PAGE_ACCESS_START(page);
470 					page->busy = true;
471 				}
472 			}
473 
474 			locker.Unlock();
475 
476 			// copy the pages to our buffer
477 			for (size_t i = 0; i < pagesToWrite; i++) {
478 				if (vm_page* page = pages[i]) {
479 					error = vm_memcpy_from_physical(buffer + i * B_PAGE_SIZE,
480 						page->physical_page_number * B_PAGE_SIZE, B_PAGE_SIZE,
481 						false);
482 					if (error != B_OK) {
483 						dprintf("ramdisk: error copying page %" B_PRIu64
484 							" data: %s\n", (uint64)page->physical_page_number,
485 							strerror(error));
486 						break;
487 					}
488 				} else
489 					memset(buffer + i * B_PAGE_SIZE, 0, B_PAGE_SIZE);
490 			}
491 
492 			// write the buffer
493 			if (error == B_OK) {
494 				ssize_t bytesWritten = pwrite(fd, buffer,
495 					pagesToWrite * B_PAGE_SIZE, offset);
496 				if (bytesWritten < 0) {
497 					dprintf("ramdisk: error writing pages to file: %s\n",
498 						strerror(bytesWritten));
499 					error = bytesWritten;
500 				}
501 				else if ((size_t)bytesWritten != pagesToWrite * B_PAGE_SIZE) {
502 					dprintf("ramdisk: error writing pages to file: short "
503 						"write (%zd/%zu)\n", bytesWritten,
504 						pagesToWrite * B_PAGE_SIZE);
505 					error = B_ERROR;
506 				}
507 			}
508 
509 			// mark the pages unbusy, on success also unmodified
510 			locker.Lock();
511 
512 			for (size_t i = 0; i < pagesToWrite; i++) {
513 				if (vm_page* page = pages[i]) {
514 					if (error == B_OK)
515 						page->modified = false;
516 					fCache->MarkPageUnbusy(page);
517 					DEBUG_PAGE_ACCESS_END(page);
518 				}
519 			}
520 
521 			if (error != B_OK)
522 				break;
523 
524 			offset += pagesToWrite * B_PAGE_SIZE;
525 		}
526 
527 		return error;
528 	}
529 
530 	status_t Trim(fs_trim_data* trimData)
531 	{
532 		TRACE("trim_device()\n");
533 
534 		uint64 trimmedSize = 0;
535 		for (uint32 i = 0; i < trimData->range_count; i++) {
536 			trimmedSize += trimData->ranges[i].size;
537 
538 			off_t offset = trimData->ranges[i].offset;
539 			off_t length = trimData->ranges[i].size;
540 
541 			// Round up offset and length to multiple of the page size
542 			// The offset is rounded up, so some space may be left
543 			// (not trimmed) at the start of the range.
544 			offset = (offset + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1);
545 			// Adjust the length for the possibly skipped range
546 			length -= trimData->ranges[i].offset - offset;
547 			// The length is rounded down, so some space at the end may also
548 			// be left (not trimmed).
549 			length &= ~(B_PAGE_SIZE - 1);
550 
551 			TRACE("ramdisk: trim %" B_PRIdOFF " bytes from %" B_PRIdOFF "\n",
552 				length, offset);
553 
554 			ASSERT(offset % B_PAGE_SIZE == 0);
555 			ASSERT(length % B_PAGE_SIZE == 0);
556 
557 			vm_page** pages = new(std::nothrow) vm_page*[length / B_PAGE_SIZE];
558 			if (pages == NULL)
559 				return B_NO_MEMORY;
560 			ArrayDeleter<vm_page*> pagesDeleter(pages);
561 
562 			_GetPages(offset, length, false, pages);
563 
564 			AutoLocker<VMCache> locker(fCache);
565 			uint32 j;
566 			for (j = 0; j < length / B_PAGE_SIZE; j++) {
567 				// If we run out of pages (some may already be trimmed), stop.
568 				if (pages[j] == NULL)
569 					break;
570 
571 				TRACE("free range %" B_PRIu32 ", page %" B_PRIu32 ", offset %"
572 					B_PRIdOFF "\n", i, j, offset);
573 				if (pages[j]->Cache())
574 					fCache->RemovePage(pages[j]);
575 				vm_page_free(NULL, pages[j]);
576 			}
577 		}
578 
579 		trimData->trimmed_size = trimmedSize;
580 
581 		return B_OK;
582 	}
583 
584 
585 
586 	status_t DoIO(IORequest* request)
587 	{
588 		return fIOScheduler->ScheduleRequest(request);
589 	}
590 
591 	virtual status_t PublishDevice()
592 	{
593 		return sDeviceManager->publish_device(Node(), fDeviceName,
594 			kRawDeviceModuleName);
595 	}
596 
597 private:
598 	static status_t _DoIOEntry(void* data, IOOperation* operation)
599 	{
600 		return ((RawDevice*)data)->_DoIO(operation);
601 	}
602 
603 	status_t _DoIO(IOOperation* operation)
604 	{
605 		off_t offset = operation->Offset();
606 		generic_size_t length = operation->Length();
607 
608 		ASSERT(offset % B_PAGE_SIZE == 0);
609 		ASSERT(length % B_PAGE_SIZE == 0);
610 
611 		const generic_io_vec* vecs = operation->Vecs();
612 		generic_size_t vecOffset = 0;
613 		bool isWrite = operation->IsWrite();
614 
615 		vm_page** pages = new(std::nothrow) vm_page*[length / B_PAGE_SIZE];
616 		if (pages == NULL)
617 			return B_NO_MEMORY;
618 		ArrayDeleter<vm_page*> pagesDeleter(pages);
619 
620 		_GetPages(offset, length, isWrite, pages);
621 
622 		status_t error = B_OK;
623 		size_t index = 0;
624 
625 		while (length > 0) {
626 			vm_page* page = pages[index];
627 
628 			if (isWrite)
629 				page->modified = true;
630 
631 			error = _CopyData(page, vecs, vecOffset, isWrite);
632 			if (error != B_OK)
633 				break;
634 
635 			offset += B_PAGE_SIZE;
636 			length -= B_PAGE_SIZE;
637 			index++;
638 		}
639 
640 		_PutPages(operation->Offset(), operation->Length(), pages,
641 			error == B_OK);
642 
643 		if (error != B_OK) {
644 			fIOScheduler->OperationCompleted(operation, error, 0);
645 			return error;
646 		}
647 
648 		fIOScheduler->OperationCompleted(operation, B_OK, operation->Length());
649 		return B_OK;
650 	}
651 
652 	void _GetPages(off_t offset, off_t length, bool isWrite, vm_page** pages)
653 	{
654 		// TODO: This method is duplicated in ramfs' DataContainer. Perhaps it
655 		// should be put into a common location?
656 
657 		// get the pages, we already have
658 		AutoLocker<VMCache> locker(fCache);
659 
660 		size_t pageCount = length / B_PAGE_SIZE;
661 		size_t index = 0;
662 		size_t missingPages = 0;
663 
664 		while (length > 0) {
665 			vm_page* page = fCache->LookupPage(offset);
666 			if (page != NULL) {
667 				if (page->busy) {
668 					fCache->WaitForPageEvents(page, PAGE_EVENT_NOT_BUSY, true);
669 					continue;
670 				}
671 
672 				DEBUG_PAGE_ACCESS_START(page);
673 				page->busy = true;
674 			} else
675 				missingPages++;
676 
677 			pages[index++] = page;
678 			offset += B_PAGE_SIZE;
679 			length -= B_PAGE_SIZE;
680 		}
681 
682 		locker.Unlock();
683 
684 		// For a write we need to reserve the missing pages.
685 		if (isWrite && missingPages > 0) {
686 			vm_page_reservation reservation;
687 			vm_page_reserve_pages(&reservation, missingPages,
688 				VM_PRIORITY_SYSTEM);
689 
690 			for (size_t i = 0; i < pageCount; i++) {
691 				if (pages[i] != NULL)
692 					continue;
693 
694 				pages[i] = vm_page_allocate_page(&reservation,
695 					PAGE_STATE_WIRED | VM_PAGE_ALLOC_BUSY);
696 
697 				if (--missingPages == 0)
698 					break;
699 			}
700 
701 			vm_page_unreserve_pages(&reservation);
702 		}
703 	}
704 
705 	void _PutPages(off_t offset, off_t length, vm_page** pages, bool success)
706 	{
707 		// TODO: This method is duplicated in ramfs' DataContainer. Perhaps it
708 		// should be put into a common location?
709 
710 		AutoLocker<VMCache> locker(fCache);
711 
712 		// Mark all pages unbusy. On error free the newly allocated pages.
713 		size_t index = 0;
714 
715 		while (length > 0) {
716 			vm_page* page = pages[index++];
717 			if (page != NULL) {
718 				if (page->CacheRef() == NULL) {
719 					if (success) {
720 						fCache->InsertPage(page, offset);
721 						fCache->MarkPageUnbusy(page);
722 						DEBUG_PAGE_ACCESS_END(page);
723 					} else
724 						vm_page_free(NULL, page);
725 				} else {
726 					fCache->MarkPageUnbusy(page);
727 					DEBUG_PAGE_ACCESS_END(page);
728 				}
729 			}
730 
731 			offset += B_PAGE_SIZE;
732 			length -= B_PAGE_SIZE;
733 		}
734 	}
735 
736 	status_t _CopyData(vm_page* page, const generic_io_vec*& vecs,
737 		generic_size_t& vecOffset, bool toPage)
738 	{
739 		// map page to virtual memory
740 		Thread* thread = thread_get_current_thread();
741 		uint8* pageData = NULL;
742 		void* handle;
743 		if (page != NULL) {
744 			thread_pin_to_current_cpu(thread);
745 			addr_t virtualAddress;
746 			status_t error = vm_get_physical_page_current_cpu(
747 				page->physical_page_number * B_PAGE_SIZE, &virtualAddress,
748 				&handle);
749 			if (error != B_OK) {
750 				thread_unpin_from_current_cpu(thread);
751 				return error;
752 			}
753 
754 			pageData = (uint8*)virtualAddress;
755 		}
756 
757 		status_t error = B_OK;
758 		size_t length = B_PAGE_SIZE;
759 		while (length > 0) {
760 			size_t toCopy = std::min((generic_size_t)length,
761 				vecs->length - vecOffset);
762 
763 			if (toCopy == 0) {
764 				vecs++;
765 				vecOffset = 0;
766 				continue;
767 			}
768 
769 			phys_addr_t vecAddress = vecs->base + vecOffset;
770 
771 			error = toPage
772 				? vm_memcpy_from_physical(pageData, vecAddress, toCopy, false)
773 				: (page != NULL
774 					? vm_memcpy_to_physical(vecAddress, pageData, toCopy, false)
775 					: vm_memset_physical(vecAddress, 0, toCopy));
776 			if (error != B_OK)
777 				break;
778 
779 			pageData += toCopy;
780 			length -= toCopy;
781 			vecOffset += toCopy;
782 		}
783 
784 		if (page != NULL) {
785 			vm_put_physical_page_current_cpu((addr_t)pageData, handle);
786 			thread_unpin_from_current_cpu(thread);
787 		}
788 
789 		return error;
790 	}
791 
792 	status_t _LoadFile()
793 	{
794 		static const size_t kPageCountPerIteration = 1024;
795 
796 		int fd = open(fFilePath, O_RDONLY);
797 		if (fd < 0)
798 			return errno;
799 		FileDescriptorCloser fdCloser(fd);
800 
801 		vm_page** pages = new(std::nothrow) vm_page*[kPageCountPerIteration];
802 		ArrayDeleter<vm_page*> pagesDeleter(pages);
803 
804 		uint8* buffer = (uint8*)malloc(kPageCountPerIteration * B_PAGE_SIZE);
805 		MemoryDeleter bufferDeleter(buffer);
806 			// TODO: Ideally we wouldn't use a buffer to read the file content,
807 			// but read into the pages we allocated directly. Unfortunately
808 			// there's no API to do that yet.
809 
810 		if (pages == NULL || buffer == NULL)
811 			return B_NO_MEMORY;
812 
813 		status_t error = B_OK;
814 
815 		page_num_t allocatedPages = 0;
816 		off_t offset = 0;
817 		off_t sizeRemaining = fDeviceSize;
818 		while (sizeRemaining > 0) {
819 			// Note: fDeviceSize is B_PAGE_SIZE aligned.
820 			size_t pagesToRead = std::min(kPageCountPerIteration,
821 				size_t(sizeRemaining / B_PAGE_SIZE));
822 
823 			// allocate the missing pages
824 			if (allocatedPages < pagesToRead) {
825 				vm_page_reservation reservation;
826 				vm_page_reserve_pages(&reservation,
827 					pagesToRead - allocatedPages, VM_PRIORITY_SYSTEM);
828 
829 				while (allocatedPages < pagesToRead) {
830 					pages[allocatedPages++]
831 						= vm_page_allocate_page(&reservation, PAGE_STATE_WIRED);
832 				}
833 
834 				vm_page_unreserve_pages(&reservation);
835 			}
836 
837 			// read from the file
838 			size_t bytesToRead = pagesToRead * B_PAGE_SIZE;
839 			ssize_t bytesRead = pread(fd, buffer, bytesToRead, offset);
840 			if (bytesRead < 0) {
841 				error = bytesRead;
842 				break;
843 			}
844 			size_t pagesRead = (bytesRead + B_PAGE_SIZE - 1) / B_PAGE_SIZE;
845 			if (pagesRead < pagesToRead) {
846 				error = B_ERROR;
847 				break;
848 			}
849 
850 			// clear the last read page, if partial
851 			if ((size_t)bytesRead < pagesRead * B_PAGE_SIZE) {
852 				memset(buffer + bytesRead, 0,
853 					pagesRead * B_PAGE_SIZE - bytesRead);
854 			}
855 
856 			// copy data to allocated pages
857 			for (size_t i = 0; i < pagesRead; i++) {
858 				vm_page* page = pages[i];
859 				error = vm_memcpy_to_physical(
860 					page->physical_page_number * B_PAGE_SIZE,
861 					buffer + i * B_PAGE_SIZE, B_PAGE_SIZE, false);
862 				if (error != B_OK)
863 					break;
864 			}
865 
866 			if (error != B_OK)
867 				break;
868 
869 			// Add pages to cache. Ignore clear pages, though. Move those to the
870 			// beginning of the array, so we can reuse them in the next
871 			// iteration.
872 			AutoLocker<VMCache> locker(fCache);
873 
874 			size_t clearPages = 0;
875 			for (size_t i = 0; i < pagesRead; i++) {
876 				uint64* pageData = (uint64*)(buffer + i * B_PAGE_SIZE);
877 				bool isClear = true;
878 				for (size_t k = 0; isClear && k < B_PAGE_SIZE / 8; k++)
879 					isClear = pageData[k] == 0;
880 
881 				if (isClear) {
882 					pages[clearPages++] = pages[i];
883 				} else {
884 					fCache->InsertPage(pages[i], offset + i * B_PAGE_SIZE);
885 					DEBUG_PAGE_ACCESS_END(pages[i]);
886 				}
887 			}
888 
889 			locker.Unlock();
890 
891 			// Move any left-over allocated pages to the end of the empty pages
892 			// and compute the new allocated pages count.
893 			if (pagesRead < allocatedPages) {
894 				size_t count = allocatedPages - pagesRead;
895 				memcpy(pages + clearPages, pages + pagesRead,
896 					count * sizeof(vm_page*));
897 				clearPages += count;
898 			}
899 			allocatedPages = clearPages;
900 
901 			offset += pagesRead * B_PAGE_SIZE;
902 			sizeRemaining -= pagesRead * B_PAGE_SIZE;
903 		}
904 
905 		// free left-over allocated pages
906 		for (size_t i = 0; i < allocatedPages; i++)
907 			vm_page_free(NULL, pages[i]);
908 
909 		return error;
910 	}
911 
912 private:
913 	int32			fID;
914 	bool			fUnregistered;
915 	off_t			fDeviceSize;
916 	char*			fDeviceName;
917 	char*			fFilePath;
918 	VMCache*		fCache;
919 	DMAResource*	fDMAResource;
920 	IOScheduler*	fIOScheduler;
921 };
922 
923 
924 struct RawDeviceCookie {
925 	RawDeviceCookie(RawDevice* device, int openMode)
926 		:
927 		fDevice(device),
928 		fOpenMode(openMode)
929 	{
930 	}
931 
932 	RawDevice* Device() const	{ return fDevice; }
933 	int OpenMode() const		{ return fOpenMode; }
934 
935 private:
936 	RawDevice*	fDevice;
937 	int			fOpenMode;
938 };
939 
940 
941 // #pragma mark -
942 
943 
944 static int32
945 allocate_raw_device_id()
946 {
947 	MutexLocker deviceListLocker(sDeviceListLock);
948 	for (size_t i = 0; i < sizeof(sUsedRawDeviceIDs) * 8; i++) {
949 		if ((sUsedRawDeviceIDs & ((uint64)1 << i)) == 0) {
950 			sUsedRawDeviceIDs |= (uint64)1 << i;
951 			return (int32)i;
952 		}
953 	}
954 
955 	return -1;
956 }
957 
958 
959 static void
960 free_raw_device_id(int32 id)
961 {
962 	MutexLocker deviceListLocker(sDeviceListLock);
963 	sUsedRawDeviceIDs &= ~((uint64)1 << id);
964 }
965 
966 
967 static RawDevice*
968 find_raw_device(int32 id)
969 {
970 	for (RawDeviceList::Iterator it = sDeviceList.GetIterator();
971 			RawDevice* device = it.Next();) {
972 		if (device->ID() == id)
973 			return device;
974 	}
975 
976 	return NULL;
977 }
978 
979 
980 static status_t
981 ioctl_register(ControlDevice* controlDevice, ram_disk_ioctl_register* request)
982 {
983 	KPath path;
984 	uint64 deviceSize = 0;
985 
986 	if (request->path[0] != '\0') {
987 		// check if the path is null-terminated
988 		if (strnlen(request->path, sizeof(request->path))
989 				== sizeof(request->path)) {
990 			return B_BAD_VALUE;
991 		}
992 
993 		// get a normalized file path
994 		status_t error = path.SetTo(request->path, true);
995 		if (error != B_OK) {
996 			dprintf("ramdisk: register: Invalid path \"%s\": %s\n",
997 				request->path, strerror(error));
998 			return B_BAD_VALUE;
999 		}
1000 
1001 		struct stat st;
1002 		if (lstat(path.Path(), &st) != 0) {
1003 			dprintf("ramdisk: register: Failed to stat \"%s\": %s\n",
1004 				path.Path(), strerror(errno));
1005 			return errno;
1006 		}
1007 
1008 		if (!S_ISREG(st.st_mode)) {
1009 			dprintf("ramdisk: register: \"%s\" is not a file!\n", path.Path());
1010 			return B_BAD_VALUE;
1011 		}
1012 
1013 		deviceSize = st.st_size;
1014 	} else {
1015 		deviceSize = request->size;
1016 	}
1017 
1018 	return controlDevice->Register(path.Length() > 0 ? path.Path() : NULL,
1019 		deviceSize, request->id);
1020 }
1021 
1022 
1023 static status_t
1024 ioctl_unregister(ControlDevice* controlDevice,
1025 	ram_disk_ioctl_unregister* request)
1026 {
1027 	// find the device in the list and unregister it
1028 	MutexLocker locker(sDeviceListLock);
1029 	RawDevice* device = find_raw_device(request->id);
1030 	if (device == NULL)
1031 		return B_ENTRY_NOT_FOUND;
1032 
1033 	// mark unregistered before we unlock
1034 	if (device->IsUnregistered())
1035 		return B_BUSY;
1036 	device->SetUnregistered(true);
1037 	locker.Unlock();
1038 
1039 	device_node* node = device->Node();
1040 	status_t error = sDeviceManager->unpublish_device(node,
1041 		device->DeviceName());
1042 	if (error != B_OK) {
1043 		dprintf("ramdisk: unregister: Failed to unpublish device \"%s\": %s\n",
1044 			device->DeviceName(), strerror(error));
1045 		return error;
1046 	}
1047 
1048 	error = sDeviceManager->unregister_node(node);
1049 	// Note: B_BUSY is OK. The node will removed as soon as possible.
1050 	if (error != B_OK && error != B_BUSY) {
1051 		dprintf("ramdisk: unregister: Failed to unregister node for device %"
1052 			B_PRId32 ": %s\n", request->id, strerror(error));
1053 		return error;
1054 	}
1055 
1056 	return B_OK;
1057 }
1058 
1059 
1060 static status_t
1061 ioctl_info(RawDevice* device, ram_disk_ioctl_info* request)
1062 {
1063 	device->GetInfo(*request);
1064 	return B_OK;
1065 }
1066 
1067 
1068 template<typename DeviceType, typename Request>
1069 static status_t
1070 handle_ioctl(DeviceType* device,
1071 	status_t (*handler)(DeviceType*, Request*), void* buffer)
1072 {
1073 	// copy request to the kernel heap
1074 	if (buffer == NULL || !IS_USER_ADDRESS(buffer))
1075 		return B_BAD_ADDRESS;
1076 
1077 	Request* request = new(std::nothrow) Request;
1078 	if (request == NULL)
1079 		return B_NO_MEMORY;
1080 	ObjectDeleter<Request> requestDeleter(request);
1081 
1082 	if (user_memcpy(request, buffer, sizeof(Request)) != B_OK)
1083 		return B_BAD_ADDRESS;
1084 
1085 	// handle the ioctl
1086 	status_t error = handler(device, request);
1087 	if (error != B_OK)
1088 		return error;
1089 
1090 	// copy the request back to userland
1091 	if (user_memcpy(buffer, request, sizeof(Request)) != B_OK)
1092 		return B_BAD_ADDRESS;
1093 
1094 	return B_OK;
1095 }
1096 
1097 
1098 //	#pragma mark - driver
1099 
1100 
1101 static float
1102 ram_disk_driver_supports_device(device_node* parent)
1103 {
1104 	const char* bus = NULL;
1105 	if (sDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false)
1106 			== B_OK
1107 		&& strcmp(bus, "generic") == 0) {
1108 		return 0.8;
1109 	}
1110 
1111 	return -1;
1112 }
1113 
1114 
1115 static status_t
1116 ram_disk_driver_register_device(device_node* parent)
1117 {
1118 	device_attr attrs[] = {
1119 		{B_DEVICE_PRETTY_NAME, B_STRING_TYPE,
1120 			{string: "RAM Disk Control Device"}},
1121 		{NULL}
1122 	};
1123 
1124 	return sDeviceManager->register_node(parent, kDriverModuleName, attrs, NULL,
1125 		NULL);
1126 }
1127 
1128 
1129 static status_t
1130 ram_disk_driver_init_driver(device_node* node, void** _driverCookie)
1131 {
1132 	uint64 deviceSize;
1133 	if (sDeviceManager->get_attr_uint64(node, kDeviceSizeItem, &deviceSize,
1134 			false) == B_OK) {
1135 		int32 id = -1;
1136 		sDeviceManager->get_attr_uint32(node, kDeviceIDItem, (uint32*)&id,
1137 			false);
1138 		if (id < 0)
1139 			return B_ERROR;
1140 
1141 		const char* filePath = NULL;
1142 		sDeviceManager->get_attr_string(node, kFilePathItem, &filePath, false);
1143 
1144 		RawDevice* device = new(std::nothrow) RawDevice(node);
1145 		if (device == NULL)
1146 			return B_NO_MEMORY;
1147 
1148 		status_t error = device->Init(id, filePath, deviceSize);
1149 		if (error != B_OK) {
1150 			delete device;
1151 			return error;
1152 		}
1153 
1154 		*_driverCookie = (Device*)device;
1155 	} else {
1156 		ControlDevice* device = new(std::nothrow) ControlDevice(node);
1157 		if (device == NULL)
1158 			return B_NO_MEMORY;
1159 
1160 		*_driverCookie = (Device*)device;
1161 	}
1162 
1163 	return B_OK;
1164 }
1165 
1166 
1167 static void
1168 ram_disk_driver_uninit_driver(void* driverCookie)
1169 {
1170 	Device* device = (Device*)driverCookie;
1171 	if (RawDevice* rawDevice = dynamic_cast<RawDevice*>(device))
1172 		free_raw_device_id(rawDevice->ID());
1173 	delete device;
1174 }
1175 
1176 
1177 static status_t
1178 ram_disk_driver_register_child_devices(void* driverCookie)
1179 {
1180 	Device* device = (Device*)driverCookie;
1181 	return device->PublishDevice();
1182 }
1183 
1184 
1185 //	#pragma mark - control device
1186 
1187 
1188 static status_t
1189 ram_disk_control_device_init_device(void* driverCookie, void** _deviceCookie)
1190 {
1191 	*_deviceCookie = driverCookie;
1192 	return B_OK;
1193 }
1194 
1195 
1196 static void
1197 ram_disk_control_device_uninit_device(void* deviceCookie)
1198 {
1199 }
1200 
1201 
1202 static status_t
1203 ram_disk_control_device_open(void* deviceCookie, const char* path, int openMode,
1204 	void** _cookie)
1205 {
1206 	*_cookie = deviceCookie;
1207 	return B_OK;
1208 }
1209 
1210 
1211 static status_t
1212 ram_disk_control_device_close(void* cookie)
1213 {
1214 	return B_OK;
1215 }
1216 
1217 
1218 static status_t
1219 ram_disk_control_device_free(void* cookie)
1220 {
1221 	return B_OK;
1222 }
1223 
1224 
1225 static status_t
1226 ram_disk_control_device_read(void* cookie, off_t position, void* buffer,
1227 	size_t* _length)
1228 {
1229 	return B_BAD_VALUE;
1230 }
1231 
1232 
1233 static status_t
1234 ram_disk_control_device_write(void* cookie, off_t position, const void* data,
1235 	size_t* _length)
1236 {
1237 	return B_BAD_VALUE;
1238 }
1239 
1240 
1241 static status_t
1242 ram_disk_control_device_control(void* cookie, uint32 op, void* buffer,
1243 	size_t length)
1244 {
1245 	ControlDevice* device = (ControlDevice*)cookie;
1246 
1247 	switch (op) {
1248 		case RAM_DISK_IOCTL_REGISTER:
1249 			return handle_ioctl(device, &ioctl_register, buffer);
1250 
1251 		case RAM_DISK_IOCTL_UNREGISTER:
1252 			return handle_ioctl(device, &ioctl_unregister, buffer);
1253 	}
1254 
1255 	return B_BAD_VALUE;
1256 }
1257 
1258 
1259 //	#pragma mark - raw device
1260 
1261 
1262 static status_t
1263 ram_disk_raw_device_init_device(void* driverCookie, void** _deviceCookie)
1264 {
1265 	RawDevice* device = static_cast<RawDevice*>((Device*)driverCookie);
1266 
1267 	status_t error = device->Prepare();
1268 	if (error != B_OK)
1269 		return error;
1270 
1271 	*_deviceCookie = device;
1272 	return B_OK;
1273 }
1274 
1275 
1276 static void
1277 ram_disk_raw_device_uninit_device(void* deviceCookie)
1278 {
1279 	RawDevice* device = (RawDevice*)deviceCookie;
1280 	device->Unprepare();
1281 }
1282 
1283 
1284 static status_t
1285 ram_disk_raw_device_open(void* deviceCookie, const char* path, int openMode,
1286 	void** _cookie)
1287 {
1288 	RawDevice* device = (RawDevice*)deviceCookie;
1289 
1290 	RawDeviceCookie* cookie = new(std::nothrow) RawDeviceCookie(device,
1291 		openMode);
1292 	if (cookie == NULL)
1293 		return B_NO_MEMORY;
1294 
1295 	*_cookie = cookie;
1296 	return B_OK;
1297 }
1298 
1299 
1300 static status_t
1301 ram_disk_raw_device_close(void* cookie)
1302 {
1303 	return B_OK;
1304 }
1305 
1306 
1307 static status_t
1308 ram_disk_raw_device_free(void* _cookie)
1309 {
1310 	RawDeviceCookie* cookie = (RawDeviceCookie*)_cookie;
1311 	delete cookie;
1312 	return B_OK;
1313 }
1314 
1315 
1316 static status_t
1317 ram_disk_raw_device_read(void* _cookie, off_t pos, void* buffer,
1318 	size_t* _length)
1319 {
1320 	RawDeviceCookie* cookie = (RawDeviceCookie*)_cookie;
1321 	RawDevice* device = cookie->Device();
1322 
1323 	size_t length = *_length;
1324 
1325 	if (pos >= device->DeviceSize())
1326 		return B_BAD_VALUE;
1327 	if (pos + (off_t)length > device->DeviceSize())
1328 		length = device->DeviceSize() - pos;
1329 
1330 	IORequest request;
1331 	status_t status = request.Init(pos, (addr_t)buffer, length, false, 0);
1332 	if (status != B_OK)
1333 		return status;
1334 
1335 	status = device->DoIO(&request);
1336 	if (status != B_OK)
1337 		return status;
1338 
1339 	status = request.Wait(0, 0);
1340 	if (status == B_OK)
1341 		*_length = length;
1342 	return status;
1343 }
1344 
1345 
1346 static status_t
1347 ram_disk_raw_device_write(void* _cookie, off_t pos, const void* buffer,
1348 	size_t* _length)
1349 {
1350 	RawDeviceCookie* cookie = (RawDeviceCookie*)_cookie;
1351 	RawDevice* device = cookie->Device();
1352 
1353 	size_t length = *_length;
1354 
1355 	if (pos >= device->DeviceSize())
1356 		return B_BAD_VALUE;
1357 	if (pos + (off_t)length > device->DeviceSize())
1358 		length = device->DeviceSize() - pos;
1359 
1360 	IORequest request;
1361 	status_t status = request.Init(pos, (addr_t)buffer, length, true, 0);
1362 	if (status != B_OK)
1363 		return status;
1364 
1365 	status = device->DoIO(&request);
1366 	if (status != B_OK)
1367 		return status;
1368 
1369 	status = request.Wait(0, 0);
1370 	if (status == B_OK)
1371 		*_length = length;
1372 
1373 	return status;
1374 }
1375 
1376 
1377 static status_t
1378 ram_disk_raw_device_io(void* _cookie, io_request* request)
1379 {
1380 	RawDeviceCookie* cookie = (RawDeviceCookie*)_cookie;
1381 	RawDevice* device = cookie->Device();
1382 
1383 	return device->DoIO(request);
1384 }
1385 
1386 
1387 static status_t
1388 ram_disk_raw_device_control(void* _cookie, uint32 op, void* buffer,
1389 	size_t length)
1390 {
1391 	RawDeviceCookie* cookie = (RawDeviceCookie*)_cookie;
1392 	RawDevice* device = cookie->Device();
1393 
1394 	switch (op) {
1395 		case B_GET_DEVICE_SIZE:
1396 		{
1397 			size_t size = device->DeviceSize();
1398 			return user_memcpy(buffer, &size, sizeof(size_t));
1399 		}
1400 
1401 		case B_SET_NONBLOCKING_IO:
1402 		case B_SET_BLOCKING_IO:
1403 			return B_OK;
1404 
1405 		case B_GET_READ_STATUS:
1406 		case B_GET_WRITE_STATUS:
1407 		{
1408 			bool value = true;
1409 			return user_memcpy(buffer, &value, sizeof(bool));
1410 		}
1411 
1412 		case B_GET_GEOMETRY:
1413 		case B_GET_BIOS_GEOMETRY:
1414 		{
1415 			device_geometry geometry;
1416 			geometry.bytes_per_sector = B_PAGE_SIZE;
1417 			geometry.sectors_per_track = 1;
1418 			geometry.cylinder_count = device->DeviceSize() / B_PAGE_SIZE;
1419 				// TODO: We're limited to 2^32 * B_PAGE_SIZE, if we don't use
1420 				// sectors_per_track and head_count.
1421 			geometry.head_count = 1;
1422 			geometry.device_type = B_DISK;
1423 			geometry.removable = true;
1424 			geometry.read_only = false;
1425 			geometry.write_once = false;
1426 
1427 			return user_memcpy(buffer, &geometry, sizeof(device_geometry));
1428 		}
1429 
1430 		case B_GET_MEDIA_STATUS:
1431 		{
1432 			status_t status = B_OK;
1433 			return user_memcpy(buffer, &status, sizeof(status_t));
1434 		}
1435 
1436 		case B_GET_ICON_NAME:
1437 			return user_strlcpy((char*)buffer, "devices/drive-ramdisk",
1438 				B_FILE_NAME_LENGTH);
1439 
1440 		case B_GET_VECTOR_ICON:
1441 		{
1442 			device_icon iconData;
1443 			if (length != sizeof(device_icon))
1444 				return B_BAD_VALUE;
1445 			if (user_memcpy(&iconData, buffer, sizeof(device_icon)) != B_OK)
1446 				return B_BAD_ADDRESS;
1447 
1448 			if (iconData.icon_size >= (int32)sizeof(kRamdiskIcon)) {
1449 				if (user_memcpy(iconData.icon_data, kRamdiskIcon,
1450 						sizeof(kRamdiskIcon)) != B_OK)
1451 					return B_BAD_ADDRESS;
1452 			}
1453 
1454 			iconData.icon_size = sizeof(kRamdiskIcon);
1455 			return user_memcpy(buffer, &iconData, sizeof(device_icon));
1456 		}
1457 
1458 		case B_SET_UNINTERRUPTABLE_IO:
1459 		case B_SET_INTERRUPTABLE_IO:
1460 		case B_FLUSH_DRIVE_CACHE:
1461 			return B_OK;
1462 
1463 		case RAM_DISK_IOCTL_FLUSH:
1464 		{
1465 			status_t error = device->Flush();
1466 			if (error != B_OK) {
1467 				dprintf("ramdisk: flush: Failed to flush device: %s\n",
1468 					strerror(error));
1469 				return error;
1470 			}
1471 
1472 			return B_OK;
1473 		}
1474 
1475 		case B_TRIM_DEVICE:
1476 		{
1477 			// We know the buffer is kernel-side because it has been
1478 			// preprocessed in devfs
1479 			ASSERT(IS_KERNEL_ADDRESS(buffer));
1480 			return device->Trim((fs_trim_data*)buffer);
1481 		}
1482 
1483 		case RAM_DISK_IOCTL_INFO:
1484 			return handle_ioctl(device, &ioctl_info, buffer);
1485 	}
1486 
1487 	return B_BAD_VALUE;
1488 }
1489 
1490 
1491 // #pragma mark -
1492 
1493 
1494 module_dependency module_dependencies[] = {
1495 	{B_DEVICE_MANAGER_MODULE_NAME, (module_info**)&sDeviceManager},
1496 	{}
1497 };
1498 
1499 
1500 static const struct driver_module_info sChecksumDeviceDriverModule = {
1501 	{
1502 		kDriverModuleName,
1503 		0,
1504 		NULL
1505 	},
1506 
1507 	ram_disk_driver_supports_device,
1508 	ram_disk_driver_register_device,
1509 	ram_disk_driver_init_driver,
1510 	ram_disk_driver_uninit_driver,
1511 	ram_disk_driver_register_child_devices
1512 };
1513 
1514 static const struct device_module_info sChecksumControlDeviceModule = {
1515 	{
1516 		kControlDeviceModuleName,
1517 		0,
1518 		NULL
1519 	},
1520 
1521 	ram_disk_control_device_init_device,
1522 	ram_disk_control_device_uninit_device,
1523 	NULL,
1524 
1525 	ram_disk_control_device_open,
1526 	ram_disk_control_device_close,
1527 	ram_disk_control_device_free,
1528 
1529 	ram_disk_control_device_read,
1530 	ram_disk_control_device_write,
1531 	NULL,	// io
1532 
1533 	ram_disk_control_device_control,
1534 
1535 	NULL,	// select
1536 	NULL	// deselect
1537 };
1538 
1539 static const struct device_module_info sChecksumRawDeviceModule = {
1540 	{
1541 		kRawDeviceModuleName,
1542 		0,
1543 		NULL
1544 	},
1545 
1546 	ram_disk_raw_device_init_device,
1547 	ram_disk_raw_device_uninit_device,
1548 	NULL,
1549 
1550 	ram_disk_raw_device_open,
1551 	ram_disk_raw_device_close,
1552 	ram_disk_raw_device_free,
1553 
1554 	ram_disk_raw_device_read,
1555 	ram_disk_raw_device_write,
1556 	ram_disk_raw_device_io,
1557 
1558 	ram_disk_raw_device_control,
1559 
1560 	NULL,	// select
1561 	NULL	// deselect
1562 };
1563 
1564 const module_info* modules[] = {
1565 	(module_info*)&sChecksumDeviceDriverModule,
1566 	(module_info*)&sChecksumControlDeviceModule,
1567 	(module_info*)&sChecksumRawDeviceModule,
1568 	NULL
1569 };
1570