vfs: pm_dumpcore: always clean up process
[minix.git] / lib / libblockdriver / driver.c
blob479c20dbf6e079e167e550f80d3f91078959839e
1 /* This file contains the device independent block driver interface.
3 * Block drivers support the following requests. Message format m10 is used.
4 * Field names are prefixed with BDEV_. Separate field names are used for the
5 * "access" and "request" fields.
7 * m_type MINOR COUNT GRANT FLAGS ID POS_LO POS_HI
8 * +--------------+--------+----------+-------+-------+------+------+------+
9 * | BDEV_OPEN | minor | access | | | id | | |
10 * |--------------+--------+----------+-------+-------+------+------+------|
11 * | BDEV_CLOSE | minor | | | | id | | |
12 * |--------------+--------+----------+-------+-------+------+------+------|
13 * | BDEV_READ | minor | bytes | grant | flags | id | position |
14 * |--------------+--------+----------+-------+-------+------+------+------|
15 * | BDEV_WRITE | minor | bytes | grant | flags | id | position |
16 * |--------------+--------+----------+-------+-------+------+------+------|
17 * | BDEV_GATHER | minor | elements | grant | flags | id | position |
18 * |--------------+--------+----------+-------+-------+------+------+------|
19 * | BDEV_SCATTER | minor | elements | grant | flags | id | position |
20 * |--------------+--------+----------+-------+-------+------+------+------|
21 * | BDEV_IOCTL | minor | request | grant | flags | id | | |
22 * -------------------------------------------------------------------------
24 * The following reply message is used for all requests.
26 * m_type STATUS ID
27 * +--------------+--------+----------+-------+-------+------+------+------+
28 * | BDEV_REPLY | status | | | | id | | |
29 * -------------------------------------------------------------------------
31 * Changes:
32 * Oct 16, 2011 split character and block protocol (D.C. van Moolenbroek)
33 * Aug 27, 2011 move common functions into driver.c (A. Welzel)
34 * Jul 25, 2005 added SYS_SIG type for signals (Jorrit N. Herder)
35 * Sep 15, 2004 added SYN_ALARM type for timeouts (Jorrit N. Herder)
36 * Jul 23, 2004 removed kernel dependencies (Jorrit N. Herder)
37 * Apr 02, 1992 constructed from AT wini and floppy driver (Kees J. Bot)
40 #include <minix/drivers.h>
41 #include <minix/blockdriver.h>
42 #include <minix/ds.h>
43 #include <sys/ioc_block.h>
44 #include <sys/ioc_disk.h>
46 #include "driver.h"
47 #include "mq.h"
48 #include "trace.h"
50 /* Management data for opened devices. */
51 static int open_devs[MAX_NR_OPEN_DEVICES];
52 static int next_open_devs_slot = 0;
54 /*===========================================================================*
55 * clear_open_devs *
56 *===========================================================================*/
57 static void clear_open_devs(void)
59 /* Reset the set of previously opened minor devices. */
60 next_open_devs_slot = 0;
63 /*===========================================================================*
64 * is_open_dev *
65 *===========================================================================*/
66 static int is_open_dev(int device)
68 /* Check whether the given minor device has previously been opened. */
69 int i;
71 for (i = 0; i < next_open_devs_slot; i++)
72 if (open_devs[i] == device)
73 return TRUE;
75 return FALSE;
78 /*===========================================================================*
79 * set_open_dev *
80 *===========================================================================*/
81 static void set_open_dev(int device)
83 /* Mark the given minor device as having been opened. */
85 if (next_open_devs_slot >= MAX_NR_OPEN_DEVICES)
86 panic("out of slots for open devices");
88 open_devs[next_open_devs_slot] = device;
89 next_open_devs_slot++;
92 /*===========================================================================*
93 * blockdriver_announce *
94 *===========================================================================*/
95 void blockdriver_announce(int type)
97 /* Announce we are up after a fresh start or a restart. */
98 int r;
99 char key[DS_MAX_KEYLEN];
100 char label[DS_MAX_KEYLEN];
101 char *driver_prefix = "drv.blk.";
103 /* Callers are allowed to use sendrec to communicate with drivers.
104 * For this reason, there may blocked callers when a driver restarts.
105 * Ask the kernel to unblock them (if any). Note that most block drivers
106 * will not restart statefully, and thus will skip this code.
108 if (type == SEF_INIT_RESTART) {
109 #if USE_STATECTL
110 if ((r = sys_statectl(SYS_STATE_CLEAR_IPC_REFS)) != OK)
111 panic("blockdriver_init: sys_statectl failed: %d", r);
112 #endif
115 /* Publish a driver up event. */
116 if ((r = ds_retrieve_label_name(label, getprocnr())) != OK)
117 panic("blockdriver_init: unable to get own label: %d", r);
119 snprintf(key, DS_MAX_KEYLEN, "%s%s", driver_prefix, label);
120 if ((r = ds_publish_u32(key, DS_DRIVER_UP, DSF_OVERWRITE)) != OK)
121 panic("blockdriver_init: unable to publish driver up event: %d", r);
123 /* Expect an open for any device before serving regular driver requests. */
124 clear_open_devs();
126 /* Initialize or reset the message queue. */
127 mq_init();
130 /*===========================================================================*
131 * blockdriver_reply *
132 *===========================================================================*/
133 void blockdriver_reply(message *m_ptr, int ipc_status, int reply)
135 /* Reply to a block request sent to the driver. */
136 endpoint_t caller_e;
137 long id;
138 int r;
140 if (reply == EDONTREPLY)
141 return;
143 caller_e = m_ptr->m_source;
144 id = m_ptr->BDEV_ID;
146 memset(m_ptr, 0, sizeof(*m_ptr));
148 m_ptr->m_type = BDEV_REPLY;
149 m_ptr->BDEV_STATUS = reply;
150 m_ptr->BDEV_ID = id;
152 /* If we would block sending the message, send it asynchronously. The NOREPLY
153 * flag is set because the caller may also issue a SENDREC (mixing sync and
154 * async comm), and the asynchronous reply could otherwise end up satisfying
155 * the SENDREC's receive part, after which our next SENDNB call would fail.
157 if (IPC_STATUS_CALL(ipc_status) == SENDREC)
158 r = sendnb(caller_e, m_ptr);
159 else
160 r = asynsend3(caller_e, m_ptr, AMF_NOREPLY);
162 if (r != OK)
163 printf("blockdriver_reply: unable to send reply to %d: %d\n",
164 caller_e, r);
167 /*===========================================================================*
168 * do_open *
169 *===========================================================================*/
170 static int do_open(struct blockdriver *bdp, message *mp)
172 /* Open a minor device. */
174 return (*bdp->bdr_open)(mp->BDEV_MINOR, mp->BDEV_ACCESS);
177 /*===========================================================================*
178 * do_close *
179 *===========================================================================*/
180 static int do_close(struct blockdriver *bdp, message *mp)
182 /* Close a minor device. */
184 return (*bdp->bdr_close)(mp->BDEV_MINOR);
187 /*===========================================================================*
188 * do_rdwt *
189 *===========================================================================*/
190 static int do_rdwt(struct blockdriver *bdp, message *mp)
192 /* Carry out a single read or write request. */
193 iovec_t iovec1;
194 u64_t position;
195 int do_write;
196 ssize_t r;
198 /* Disk address? Address and length of the user buffer? */
199 if (mp->BDEV_COUNT < 0) return EINVAL;
201 /* Create a one element scatter/gather vector for the buffer. */
202 iovec1.iov_addr = mp->BDEV_GRANT;
203 iovec1.iov_size = mp->BDEV_COUNT;
205 /* Transfer bytes from/to the device. */
206 do_write = (mp->m_type == BDEV_WRITE);
207 position = make64(mp->BDEV_POS_LO, mp->BDEV_POS_HI);
209 r = (*bdp->bdr_transfer)(mp->BDEV_MINOR, do_write, position, mp->m_source,
210 &iovec1, 1, mp->BDEV_FLAGS);
212 /* Return the number of bytes transferred or an error code. */
213 return r;
216 /*===========================================================================*
217 * do_vrdwt *
218 *===========================================================================*/
219 static int do_vrdwt(struct blockdriver *bdp, message *mp, thread_id_t id)
221 /* Carry out an device read or write to/from a vector of buffers. */
222 iovec_t iovec[NR_IOREQS];
223 unsigned nr_req;
224 u64_t position;
225 int i, do_write;
226 ssize_t r, size;
228 /* Copy the vector from the caller to kernel space. */
229 nr_req = mp->BDEV_COUNT; /* Length of I/O vector */
230 if (nr_req > NR_IOREQS) nr_req = NR_IOREQS;
232 if (OK != sys_safecopyfrom(mp->m_source, (vir_bytes) mp->BDEV_GRANT,
233 0, (vir_bytes) iovec, nr_req * sizeof(iovec[0]))) {
234 printf("blockdriver: bad I/O vector by: %d\n", mp->m_source);
235 return EINVAL;
238 /* Check for overflow condition, and update the size for block tracing. */
239 for (i = size = 0; i < nr_req; i++) {
240 if ((ssize_t) (size + iovec[i].iov_size) < size) return EINVAL;
241 size += iovec[i].iov_size;
244 trace_setsize(id, size);
246 /* Transfer bytes from/to the device. */
247 do_write = (mp->m_type == BDEV_SCATTER);
248 position = make64(mp->BDEV_POS_LO, mp->BDEV_POS_HI);
250 r = (*bdp->bdr_transfer)(mp->BDEV_MINOR, do_write, position, mp->m_source,
251 iovec, nr_req, mp->BDEV_FLAGS);
253 /* Return the number of bytes transferred or an error code. */
254 return r;
257 /*===========================================================================*
258 * do_dioctl *
259 *===========================================================================*/
260 static int do_dioctl(struct blockdriver *bdp, dev_t minor,
261 unsigned int request, endpoint_t endpt, cp_grant_id_t grant)
263 /* Carry out a disk-specific I/O control request. */
264 struct device *dv;
265 struct partition entry;
266 int r = EINVAL;
268 switch (request) {
269 case DIOCSETP:
270 /* Copy just this one partition table entry. */
271 r = sys_safecopyfrom(endpt, grant, 0, (vir_bytes) &entry,
272 sizeof(entry));
273 if (r != OK)
274 return r;
276 if ((dv = (*bdp->bdr_part)(minor)) == NULL)
277 return ENXIO;
278 dv->dv_base = entry.base;
279 dv->dv_size = entry.size;
281 break;
283 case DIOCGETP:
284 /* Return a partition table entry and the geometry of the drive. */
285 if ((dv = (*bdp->bdr_part)(minor)) == NULL)
286 return ENXIO;
287 entry.base = dv->dv_base;
288 entry.size = dv->dv_size;
289 if (bdp->bdr_geometry) {
290 (*bdp->bdr_geometry)(minor, &entry);
291 } else {
292 /* The driver doesn't care -- make up fake geometry. */
293 entry.cylinders = div64u(entry.size, SECTOR_SIZE);
294 entry.heads = 64;
295 entry.sectors = 32;
298 r = sys_safecopyto(endpt, grant, 0, (vir_bytes) &entry, sizeof(entry));
300 break;
303 return r;
306 /*===========================================================================*
307 * do_ioctl *
308 *===========================================================================*/
309 static int do_ioctl(struct blockdriver *bdp, message *mp)
311 /* Carry out an I/O control request. We forward block trace control requests
312 * to the tracing module, and handle setting/getting partitions when the driver
313 * has specified that it is a disk driver.
315 dev_t minor;
316 unsigned int request;
317 cp_grant_id_t grant;
318 int r;
320 minor = mp->BDEV_MINOR;
321 request = mp->BDEV_REQUEST;
322 grant = mp->BDEV_GRANT;
324 switch (request) {
325 case BIOCTRACEBUF:
326 case BIOCTRACECTL:
327 case BIOCTRACEGET:
328 /* Block trace control. */
329 r = trace_ctl(minor, request, mp->m_source, grant);
331 break;
333 case DIOCSETP:
334 case DIOCGETP:
335 /* Handle disk-specific IOCTLs only for disk-type drivers. */
336 if (bdp->bdr_type == BLOCKDRIVER_TYPE_DISK) {
337 /* Disk partition control. */
338 r = do_dioctl(bdp, minor, request, mp->m_source, grant);
340 break;
343 /* fall-through */
344 default:
345 if (bdp->bdr_ioctl)
346 r = (*bdp->bdr_ioctl)(minor, request, mp->m_source, grant);
347 else
348 r = EINVAL;
351 return r;
354 /*===========================================================================*
355 * blockdriver_handle_notify *
356 *===========================================================================*/
357 void blockdriver_handle_notify(struct blockdriver *bdp, message *m_ptr)
359 /* Take care of the notifications (interrupt and clock messages) by calling
360 * the appropiate callback functions. Never send a reply.
363 /* Call the appropriate function for this notification. */
364 switch (_ENDPOINT_P(m_ptr->m_source)) {
365 case HARDWARE:
366 if (bdp->bdr_intr)
367 (*bdp->bdr_intr)(m_ptr->NOTIFY_ARG);
368 break;
370 case CLOCK:
371 if (bdp->bdr_alarm)
372 (*bdp->bdr_alarm)(m_ptr->NOTIFY_TIMESTAMP);
373 break;
375 default:
376 if (bdp->bdr_other)
377 (void) (*bdp->bdr_other)(m_ptr);
381 /*===========================================================================*
382 * blockdriver_handle_request *
383 *===========================================================================*/
384 int blockdriver_handle_request(struct blockdriver *bdp, message *m_ptr,
385 thread_id_t id)
387 /* Call the appropiate driver function, based on the type of request. Return
388 * the result code that is to be sent back to the caller, or EDONTREPLY if no
389 * reply is to be sent.
391 int r;
393 /* We might get spurious requests if the driver has been restarted. Deny any
394 * requests on devices that have not previously been opened, signaling the
395 * caller that something went wrong.
397 if (IS_BDEV_RQ(m_ptr->m_type) && !is_open_dev(m_ptr->BDEV_MINOR)) {
398 /* Reply ERESTART to spurious requests for unopened devices. */
399 if (m_ptr->m_type != BDEV_OPEN)
400 return ERESTART;
402 /* Mark the device as opened otherwise. */
403 set_open_dev(m_ptr->BDEV_MINOR);
406 trace_start(id, m_ptr);
408 /* Call the appropriate function(s) for this request. */
409 switch (m_ptr->m_type) {
410 case BDEV_OPEN: r = do_open(bdp, m_ptr); break;
411 case BDEV_CLOSE: r = do_close(bdp, m_ptr); break;
412 case BDEV_READ:
413 case BDEV_WRITE: r = do_rdwt(bdp, m_ptr); break;
414 case BDEV_GATHER:
415 case BDEV_SCATTER: r = do_vrdwt(bdp, m_ptr, id); break;
416 case BDEV_IOCTL: r = do_ioctl(bdp, m_ptr); break;
417 default:
418 if (bdp->bdr_other)
419 r = bdp->bdr_other(m_ptr);
420 else
421 r = EINVAL;
424 /* Let the driver perform any cleanup. */
425 if (bdp->bdr_cleanup != NULL)
426 (*bdp->bdr_cleanup)();
428 trace_finish(id, r);
430 return r;