+static int
+vn_read_swapfile(
+ struct vnode *vp,
+ uio_t uio)
+{
+ static char *swap_read_zero_page = NULL;
+ int error;
+ off_t swap_count, this_count;
+ off_t file_end, read_end;
+ off_t prev_resid;
+
+ /*
+ * Reading from a swap file will get you all zeroes.
+ */
+ error = 0;
+ swap_count = uio_resid(uio);
+
+ file_end = ubc_getsize(vp);
+ read_end = uio->uio_offset + uio_resid(uio);
+ if (uio->uio_offset >= file_end) {
+ /* uio starts after end of file: nothing to read */
+ swap_count = 0;
+ } else if (read_end > file_end) {
+ /* uio extends beyond end of file: stop before that */
+ swap_count -= (read_end - file_end);
+ }
+
+ while (swap_count > 0) {
+ if (swap_read_zero_page == NULL) {
+ char *my_zero_page;
+ int funnel_state;
+
+ /*
+ * Take kernel funnel so that only one thread
+ * sets up "swap_read_zero_page".
+ */
+ funnel_state = thread_funnel_set(kernel_flock, TRUE);
+
+ if (swap_read_zero_page == NULL) {
+ MALLOC(my_zero_page, char *, PAGE_SIZE,
+ M_TEMP, M_WAITOK);
+ memset(my_zero_page, '?', PAGE_SIZE);
+ /*
+ * Adding a newline character here
+ * and there prevents "less(1)", for
+ * example, from getting too confused
+ * about a file with one really really
+ * long line.
+ */
+ my_zero_page[PAGE_SIZE-1] = '\n';
+ if (swap_read_zero_page == NULL) {
+ swap_read_zero_page = my_zero_page;
+ } else {
+ FREE(my_zero_page, M_TEMP);
+ }
+ } else {
+ /*
+ * Someone else raced us here and won;
+ * just use their page.
+ */
+ }
+ thread_funnel_set(kernel_flock, funnel_state);
+ }
+
+ this_count = swap_count;
+ if (this_count > PAGE_SIZE) {
+ this_count = PAGE_SIZE;
+ }
+
+ prev_resid = uio_resid(uio);
+ error = uiomove((caddr_t) swap_read_zero_page,
+ this_count,
+ uio);
+ if (error) {
+ break;
+ }
+ swap_count -= (prev_resid - uio_resid(uio));
+ }
+
+ return error;
+}