pager.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385
  1. /*
  2. *
  3. * Copyright (c) 2003 The Regents of the University of California. All
  4. * rights reserved.
  5. *
  6. * Redistribution and use in source and binary forms, with or without
  7. * modification, are permitted provided that the following conditions
  8. * are met:
  9. *
  10. * - Redistributions of source code must retain the above copyright
  11. * notice, this list of conditions and the following disclaimer.
  12. *
  13. * - Neither the name of the University nor the names of its
  14. * contributors may be used to endorse or promote products derived
  15. * from this software without specific prior written permission.
  16. *
  17. * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS''
  18. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
  19. * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
  20. * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
  21. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  22. * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  23. * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  24. * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
  25. * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  26. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  27. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  28. *
  29. */
  30. /*
  31. * FUSD - The Framework for UserSpace Devices - Example program
  32. *
  33. * Jeremy Elson <jelson@circlemud.org>
  34. *
  35. * pagerd: simple daemon to accept page signals from underlying page
  36. * devices, and redistribute those pages to applications.
  37. *
  38. * The application itself is not especially useful, but this example
  39. * program has proved very valuable as a generic template for FUSD
  40. * drivers that service multiple clients, and implement both blocking
  41. * and selectable devices. This file is a good place to start for
  42. * writing drivers. See logring.c for a more complex real-world
  43. * application based on this template.
  44. *
  45. * How to use the pager:
  46. *
  47. * Interface for devices that generate pages: write "page" to
  48. * /dev/pager/input
  49. *
  50. * Interface for programs waiting for pages: read from (or, select on
  51. * and then read from) /dev/pager/notify. reads will unblock when a
  52. * page arrives. Note that if more than one page arrives before you
  53. * read, you'll only get the most recent one. In other words, you are
  54. * guaranteed to get at least one page.
  55. *
  56. * Important: in order to guarantee that you do not miss any pages,
  57. * you MUST NOT close the file descriptor in between reads/selects.
  58. * If you close the FD and then reopen it, there will be a race (pages
  59. * that arrive between the close and open will not be delivered).
  60. *
  61. */
  62. #include <stdio.h>
  63. #include <stdlib.h>
  64. #include <string.h>
  65. #include <errno.h>
  66. #include <fcntl.h>
  67. #include <fusd.h>
  68. /* EXAMPLE START pager-open.c */
  69. /* per-client structure to keep track of who has an open FD to us */
  70. struct pager_client {
  71. int last_page_seen; /* seq. no. of last page this client has seen */
  72. struct fusd_file_info *read; /* outstanding read request, if any */
  73. struct fusd_file_info *polldiff; /* outstanding polldiff request */
  74. struct pager_client *next; /* to construct the linked list */
  75. };
  76. struct pager_client *client_list = NULL; /* list of clients (open FDs) */
  77. int last_page = 0; /* seq. no. of the most recent page to arrive */
  78. /* EXAMPLE STOP pager-open.c */
  79. void pager_notify_complete_read(struct pager_client *c);
  80. void pager_notify_complete_polldiff(struct pager_client *c);
  81. /************************************************************************/
  82. /*
  83. * this function removes an element from a linked list. the
  84. * pointer-manipulation insanity below is a trick that prevents the
  85. * "element to be removed is the head of the list" from being a
  86. * special case.
  87. */
  88. void client_list_remove(struct pager_client *c)
  89. {
  90. struct pager_client **ptr;
  91. if (c == NULL || client_list == NULL)
  92. return;
  93. for (ptr = &client_list; *ptr != c; ptr = &((**ptr).next)) {
  94. if (!*ptr) {
  95. fprintf(stderr, "trying to remove a client that isn't in the list\n");
  96. return;
  97. }
  98. }
  99. *ptr = c->next;
  100. }
  101. /* EXAMPLE START pager-open.c */
  102. /* open on /dev/pager/notify: create state for this client */
  103. static int pager_notify_open(struct fusd_file_info *file)
  104. {
  105. /* create state for this client */
  106. struct pager_client *c = malloc(sizeof(struct pager_client));
  107. if (c == NULL)
  108. return -ENOBUFS;
  109. /* initialize fields of this client state */
  110. memset(c, 0, sizeof(struct pager_client));
  111. c->last_page_seen = last_page;
  112. /* save the pointer to this state so it gets returned to us later */
  113. file->private_data = c;
  114. /* add this client to the client list */
  115. c->next = client_list;
  116. client_list = c;
  117. return 0;
  118. }
  119. /* EXAMPLE STOP pager-open.c */
  120. /* EXAMPLE START pager-close.c */
  121. /* close on /dev/pager/notify: destroy state for this client */
  122. static int pager_notify_close(struct fusd_file_info *file)
  123. {
  124. struct pager_client *c;
  125. if ((c = (struct pager_client *) file->private_data) != NULL) {
  126. /* take this client off our client list */
  127. client_list_remove(c);
  128. /* if there is a read outstanding, free the state */
  129. if (c->read != NULL) {
  130. fusd_destroy(c->read);
  131. c->read = NULL;
  132. }
  133. /* destroy any outstanding polldiffs */
  134. if (c->polldiff != NULL) {
  135. fusd_destroy(c->polldiff);
  136. c->polldiff = NULL;
  137. }
  138. /* get rid of the struct */
  139. free(c);
  140. file->private_data = NULL;
  141. }
  142. return 0;
  143. }
  144. /* EXAMPLE STOP pager-close.c */
  145. /*
  146. * read on /dev/pager/notify: store the fusd_file_info pointer. then call
  147. * complete_read, which will immediately call fusd_return, if there is
  148. * a page already waiting.
  149. *
  150. * Note that this shows a trick we use commonly in FUSD drivers: you
  151. * are allowed to call fusd_return() from within a callback as long as
  152. * you return -FUSD_NOREPLY. In other words, a driver can EITHER
  153. * return a real return value from its callback, OR call fusd_return
  154. * explicitly, but not both.
  155. */
  156. /* EXAMPLE START pager-read.c */
  157. ssize_t pager_notify_read(struct fusd_file_info *file, char *buffer,
  158. size_t len, loff_t *offset)
  159. {
  160. struct pager_client *c = (struct pager_client *) file->private_data;
  161. if (c == NULL || c->read != NULL) {
  162. fprintf(stderr, "pager_read's arguments are confusd, alas");
  163. return -EINVAL;
  164. }
  165. c->read = file;
  166. pager_notify_complete_read(c);
  167. return -FUSD_NOREPLY;
  168. }
  169. /* EXAMPLE STOP pager-read.c */
  170. /*
  171. * This function "completes" a read: that is, matches up a client who
  172. * is requesting data with data that's waiting to be served.
  173. *
  174. * This function is called in two cases:
  175. *
  176. * 1- When a new read request comes in. The driver might be able to
  177. * complete immediately, if a page arrived between the time the
  178. * process opened the device and performed the read. This is the
  179. * common case for clients that use select. hasn't seen yet - this
  180. * is normal if )
  181. *
  182. * 2- When a new page arrives, all readers are unblocked
  183. */
  184. /* EXAMPLE START pager-read.c */
  185. void pager_notify_complete_read(struct pager_client *c)
  186. {
  187. /* if there is no outstanding read, do nothing */
  188. if (c == NULL || c->read == NULL)
  189. return;
  190. /* if there are no outstanding pages, do nothing */
  191. if (c->last_page_seen >= last_page)
  192. return;
  193. /* bring this client up to date with the most recent page */
  194. c->last_page_seen = last_page;
  195. /* and notify the client by unblocking the read (read returns 0) */
  196. fusd_return(c->read, 0);
  197. c->read = NULL;
  198. }
  199. /* EXAMPLE STOP pager-read.c */
  200. /* This function is only called on behalf of clients who are trying to
  201. * use select(). The kernel keeps us up to date on what it thinks the
  202. * current "poll state" is, i.e. readable and/or writable. The kernel
  203. * calls this function every time its assumption about the current
  204. * poll state changes. Every time the driver's notion of the state
  205. * differs from what the kernel's cached value, it should return the
  206. * poll_diff request with the updated state. Note that a 2nd request
  207. * may come from the kernel before the driver has returned the first
  208. * one; if this happens, use fusd_destroy() to get rid of the older one.
  209. */
  210. /* EXAMPLE START pager-polldiff.c */
  211. int pager_notify_polldiff(struct fusd_file_info *file,
  212. unsigned int cached_state)
  213. {
  214. struct pager_client *c = (struct pager_client *) file->private_data;
  215. if (c == NULL)
  216. return -EINVAL;
  217. /* if we're already holding a polldiff request that we haven't
  218. * replied to yet, destroy the old one and hold onto only the new
  219. * one */
  220. if (c->polldiff != NULL) {
  221. fusd_destroy(c->polldiff);
  222. c->polldiff = NULL;
  223. }
  224. c->polldiff = file;
  225. pager_notify_complete_polldiff(c);
  226. return -FUSD_NOREPLY;
  227. }
  228. /* EXAMPLE STOP pager-polldiff.c */
  229. /*
  230. * complete_polldiff: if a client has an outstanding 'polldiff'
  231. * request, possibly return updated poll-state information to the
  232. * kernel, if indeed the state has changed.
  233. */
  234. /* EXAMPLE START pager-polldiff.c */
  235. void pager_notify_complete_polldiff(struct pager_client *c)
  236. {
  237. int curr_state, cached_state;
  238. /* if there is no outstanding polldiff, do nothing */
  239. if (c == NULL || c->polldiff == NULL)
  240. return;
  241. /* figure out the "current" state: i.e. whether or not the pager
  242. * is readable for this client based on the last page it saw */
  243. if (c->last_page_seen < last_page)
  244. curr_state = FUSD_NOTIFY_INPUT; /* readable */
  245. else
  246. curr_state = 0; /* not readable or writable */
  247. /* cached_state is what the kernel *thinks* the state is */
  248. cached_state = fusd_get_poll_diff_cached_state(c->polldiff);
  249. /* if the state is not what the kernel thinks it is, notify the
  250. kernel of the change */
  251. if (curr_state != cached_state) {
  252. fusd_return(c->polldiff, curr_state);
  253. c->polldiff = NULL;
  254. }
  255. }
  256. /* EXAMPLE STOP pager-polldiff.c */
  257. /*
  258. * this handles a write on /dev/pager/input. this is called by one of
  259. * the underlying page devices when a page arrives. if a device
  260. * writes "page" to this interface, a page is queued for everyone
  261. * using the notify interface.
  262. */
  263. #define CASE(x) if ((found == 0) && !strcmp(tmp, x) && (found = 1))
  264. /* EXAMPLE START pager-read.c */
  265. ssize_t pager_input_write(struct fusd_file_info *file,
  266. const char *buffer, size_t len, loff_t *offset)
  267. {
  268. struct pager_client *c;
  269. /* ... */
  270. /* EXAMPLE STOP pager-read.c */
  271. char tmp[1024];
  272. int found = 0;
  273. if (len > sizeof(tmp) - 1)
  274. len = sizeof(tmp) - 1;
  275. strncpy(tmp, buffer, len);
  276. tmp[len] = '\0';
  277. /* strip trailing \n's */
  278. while (tmp[len-1] == '\n')
  279. tmp[--len] = '\0';
  280. /* EXAMPLE START pager-read.c */
  281. CASE("page") {
  282. last_page++;
  283. for (c = client_list; c != NULL; c = c->next) {
  284. pager_notify_complete_polldiff(c);
  285. pager_notify_complete_read(c);
  286. }
  287. }
  288. /* EXAMPLE STOP pager-read.c */
  289. /* other commands (if there ever are any) can go here */
  290. if (!found)
  291. return -EINVAL;
  292. else
  293. return len;
  294. }
  295. #undef CASE
  296. static int fusd_success(struct fusd_file_info *file)
  297. {
  298. return 0;
  299. }
  300. int main(int argc, char *argv[])
  301. {
  302. /* register the input device */
  303. fusd_simple_register("/dev/pager/input", "pager", "pager!input", 0666, NULL,
  304. open: fusd_success, close: fusd_success,
  305. write: pager_input_write);
  306. /* register the notification device */
  307. fusd_simple_register("/dev/pager/notify", "pager", "pager!notify", 0666, NULL,
  308. open: pager_notify_open,
  309. close: pager_notify_close,
  310. read: pager_notify_read,
  311. poll_diff: pager_notify_polldiff);
  312. printf("calling fusd_run; reads from /dev/pager/notify will now block\n"
  313. "until someone writes 'page' to /dev/pager/input...\n");
  314. fusd_run();
  315. return 0;
  316. }