diff options
author | David S. Miller <davem@davemloft.net> | 2010-01-22 22:45:46 -0800 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-01-22 22:45:46 -0800 |
commit | 6be325719b3e54624397e413efd4b33a997e55a3 (patch) | |
tree | 57f321a56794cab2222e179b16731e0d76a4a68a /drivers/staging/dst/export.c | |
parent | 26d92f9276a56d55511a427fb70bd70886af647a (diff) | |
parent | 92dcffb916d309aa01778bf8963a6932e4014d07 (diff) |
Merge branch 'master' of /home/davem/src/GIT/linux-2.6/
Diffstat (limited to 'drivers/staging/dst/export.c')
-rw-r--r-- | drivers/staging/dst/export.c | 660 |
1 files changed, 0 insertions, 660 deletions
diff --git a/drivers/staging/dst/export.c b/drivers/staging/dst/export.c deleted file mode 100644 index c324230e8b6..00000000000 --- a/drivers/staging/dst/export.c +++ /dev/null @@ -1,660 +0,0 @@ -/* - * 2007+ Copyright (c) Evgeniy Polyakov <zbr@ioremap.net> - * All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include <linux/blkdev.h> -#include <linux/bio.h> -#include <linux/dst.h> -#include <linux/in.h> -#include <linux/in6.h> -#include <linux/poll.h> -#include <linux/slab.h> -#include <linux/socket.h> - -#include <net/sock.h> - -/* - * Export bioset is used for server block IO requests. - */ -static struct bio_set *dst_bio_set; - -int __init dst_export_init(void) -{ - int err = -ENOMEM; - - dst_bio_set = bioset_create(32, sizeof(struct dst_export_priv)); - if (!dst_bio_set) - goto err_out_exit; - - return 0; - -err_out_exit: - return err; -} - -void dst_export_exit(void) -{ - bioset_free(dst_bio_set); -} - -/* - * When client connects and autonegotiates with the server node, - * its permissions are checked in a security attributes and sent - * back. - */ -static unsigned int dst_check_permissions(struct dst_state *main, - struct dst_state *st) -{ - struct dst_node *n = main->node; - struct dst_secure *sentry; - struct dst_secure_user *s; - struct saddr *sa = &st->ctl.addr; - unsigned int perm = 0; - - mutex_lock(&n->security_lock); - list_for_each_entry(sentry, &n->security_list, sec_entry) { - s = &sentry->sec; - - if (s->addr.sa_family != sa->sa_family) - continue; - - if (s->addr.sa_data_len != sa->sa_data_len) - continue; - - /* - * This '2' below is a port field. This may be very wrong to do - * in atalk for example though. If there will be any need - * to extent protocol to something else, I can create - * per-family helpers and use them instead of this memcmp. - */ - if (memcmp(s->addr.sa_data + 2, sa->sa_data + 2, - sa->sa_data_len - 2)) - continue; - - perm = s->permissions; - } - mutex_unlock(&n->security_lock); - - return perm; -} - -/* - * Accept new client: allocate appropriate network state and check permissions. - */ -static struct dst_state *dst_accept_client(struct dst_state *st) -{ - unsigned int revents = 0; - unsigned int err_mask = POLLERR | POLLHUP | POLLRDHUP; - unsigned int mask = err_mask | POLLIN; - struct dst_node *n = st->node; - int err = 0; - struct socket *sock = NULL; - struct dst_state *new; - - while (!err && !sock) { - revents = dst_state_poll(st); - - if (!(revents & mask)) { - DEFINE_WAIT(wait); - - for (;;) { - prepare_to_wait(&st->thread_wait, - &wait, TASK_INTERRUPTIBLE); - if (!n->trans_scan_timeout || st->need_exit) - break; - - revents = dst_state_poll(st); - - if (revents & mask) - break; - - if (signal_pending(current)) - break; - - /* - * Magic HZ? Polling check above is not safe in - * all cases (like socket reset in BH context), - * so it is simpler just to postpone it to the - * process context instead of implementing - * special locking there. - */ - schedule_timeout(HZ); - } - finish_wait(&st->thread_wait, &wait); - } - - err = -ECONNRESET; - dst_state_lock(st); - - dprintk("%s: st: %p, revents: %x [err: %d, in: %d].\n", - __func__, st, revents, revents & err_mask, - revents & POLLIN); - - if (revents & err_mask) { - dprintk("%s: revents: %x, socket: %p, err: %d.\n", - __func__, revents, st->socket, err); - err = -ECONNRESET; - } - - if (!n->trans_scan_timeout || st->need_exit) - err = -ENODEV; - - if (st->socket && (revents & POLLIN)) - err = kernel_accept(st->socket, &sock, 0); - - dst_state_unlock(st); - } - - if (err) - goto err_out_exit; - - new = dst_state_alloc(st->node); - if (IS_ERR(new)) { - err = -ENOMEM; - goto err_out_release; - } - new->socket = sock; - - new->ctl.addr.sa_data_len = sizeof(struct sockaddr); - err = kernel_getpeername(sock, (struct sockaddr *)&new->ctl.addr, - (int *)&new->ctl.addr.sa_data_len); - if (err) - goto err_out_put; - - new->permissions = dst_check_permissions(st, new); - if (new->permissions == 0) { - err = -EPERM; - dst_dump_addr(sock, (struct sockaddr *)&new->ctl.addr, - "Client is not allowed to connect"); - goto err_out_put; - } - - err = dst_poll_init(new); - if (err) - goto err_out_put; - - dst_dump_addr(sock, (struct sockaddr *)&new->ctl.addr, - "Connected client"); - - return new; - -err_out_put: - dst_state_put(new); -err_out_release: - sock_release(sock); -err_out_exit: - return ERR_PTR(err); -} - -/* - * Each server's block request sometime finishes. - * Usually it happens in hard irq context of the appropriate controller, - * so to play good with all cases we just queue BIO into the queue - * and wake up processing thread, which gets completed request and - * send (encrypting if needed) it back to the client (if it was a read - * request), or sends back reply that writing successfully completed. - */ -static int dst_export_process_request_queue(struct dst_state *st) -{ - unsigned long flags; - struct dst_export_priv *p = NULL; - struct bio *bio; - int err = 0; - - while (!list_empty(&st->request_list)) { - spin_lock_irqsave(&st->request_lock, flags); - if (!list_empty(&st->request_list)) { - p = list_first_entry(&st->request_list, - struct dst_export_priv, request_entry); - list_del(&p->request_entry); - } - spin_unlock_irqrestore(&st->request_lock, flags); - - if (!p) - break; - - bio = p->bio; - - if (dst_need_crypto(st->node) && (bio_data_dir(bio) == READ)) - err = dst_export_crypto(st->node, bio); - else - err = dst_export_send_bio(bio); - - if (err) - break; - } - - return err; -} - -/* - * Cleanup export state. - * It has to wait until all requests are finished, - * and then free them all. - */ -static void dst_state_cleanup_export(struct dst_state *st) -{ - struct dst_export_priv *p; - unsigned long flags; - - /* - * This loop waits for all pending bios to be completed and freed. - */ - while (atomic_read(&st->refcnt) > 1) { - dprintk("%s: st: %p, refcnt: %d, list_empty: %d.\n", - __func__, st, atomic_read(&st->refcnt), - list_empty(&st->request_list)); - wait_event_timeout(st->thread_wait, - (atomic_read(&st->refcnt) == 1) || - !list_empty(&st->request_list), - HZ/2); - - while (!list_empty(&st->request_list)) { - p = NULL; - spin_lock_irqsave(&st->request_lock, flags); - if (!list_empty(&st->request_list)) { - p = list_first_entry(&st->request_list, - struct dst_export_priv, request_entry); - list_del(&p->request_entry); - } - spin_unlock_irqrestore(&st->request_lock, flags); - - if (p) - bio_put(p->bio); - - dprintk("%s: st: %p, refcnt: %d, list_empty: %d, p: " - "%p.\n", __func__, st, atomic_read(&st->refcnt), - list_empty(&st->request_list), p); - } - } - - dst_state_put(st); -} - -/* - * Client accepting thread. - * Not only accepts new connection, but also schedules receiving thread - * and performs request completion described above. - */ -static int dst_accept(void *init_data, void *schedule_data) -{ - struct dst_state *main_st = schedule_data; - struct dst_node *n = init_data; - struct dst_state *st; - int err; - - while (n->trans_scan_timeout && !main_st->need_exit) { - dprintk("%s: main_st: %p, n: %p.\n", __func__, main_st, n); - st = dst_accept_client(main_st); - if (IS_ERR(st)) - continue; - - err = dst_state_schedule_receiver(st); - if (!err) { - while (n->trans_scan_timeout) { - err = wait_event_interruptible_timeout(st->thread_wait, - !list_empty(&st->request_list) || - !n->trans_scan_timeout || - st->need_exit, - HZ); - - if (!n->trans_scan_timeout || st->need_exit) - break; - - if (list_empty(&st->request_list)) - continue; - - err = dst_export_process_request_queue(st); - if (err) - break; - } - - st->need_exit = 1; - wake_up(&st->thread_wait); - } - - dst_state_cleanup_export(st); - } - - dprintk("%s: freeing listening socket st: %p.\n", __func__, main_st); - - dst_state_lock(main_st); - dst_poll_exit(main_st); - dst_state_socket_release(main_st); - dst_state_unlock(main_st); - dst_state_put(main_st); - dprintk("%s: freed listening socket st: %p.\n", __func__, main_st); - - return 0; -} - -int dst_start_export(struct dst_node *n) -{ - if (list_empty(&n->security_list)) { - printk(KERN_ERR "You are trying to export node '%s' " - "without security attributes.\nNo clients will " - "be allowed to connect. Exiting.\n", n->name); - return -EINVAL; - } - return dst_node_trans_init(n, sizeof(struct dst_export_priv)); -} - -/* - * Initialize listening state and schedule accepting thread. - */ -int dst_node_init_listened(struct dst_node *n, struct dst_export_ctl *le) -{ - struct dst_state *st; - int err = -ENOMEM; - struct dst_network_ctl *ctl = &le->ctl; - - memcpy(&n->info->net, ctl, sizeof(struct dst_network_ctl)); - - st = dst_state_alloc(n); - if (IS_ERR(st)) { - err = PTR_ERR(st); - goto err_out_exit; - } - memcpy(&st->ctl, ctl, sizeof(struct dst_network_ctl)); - - err = dst_state_socket_create(st); - if (err) - goto err_out_put; - - st->socket->sk->sk_reuse = 1; - - err = kernel_bind(st->socket, (struct sockaddr *)&ctl->addr, - ctl->addr.sa_data_len); - if (err) - goto err_out_socket_release; - - err = kernel_listen(st->socket, 1024); - if (err) - goto err_out_socket_release; - n->state = st; - - err = dst_poll_init(st); - if (err) - goto err_out_socket_release; - - dst_state_get(st); - - err = thread_pool_schedule(n->pool, dst_thread_setup, - dst_accept, st, MAX_SCHEDULE_TIMEOUT); - if (err) - goto err_out_poll_exit; - - return 0; - -err_out_poll_exit: - dst_poll_exit(st); -err_out_socket_release: - dst_state_socket_release(st); -err_out_put: - dst_state_put(st); -err_out_exit: - n->state = NULL; - return err; -} - -/* - * Free bio and related private data. - * Also drop a reference counter for appropriate state, - * which waits when there are no more block IOs in-flight. - */ -static void dst_bio_destructor(struct bio *bio) -{ - struct bio_vec *bv; - struct dst_export_priv *priv = bio->bi_private; - int i; - - bio_for_each_segment(bv, bio, i) { - if (!bv->bv_page) - break; - - __free_page(bv->bv_page); - } - - if (priv) - dst_state_put(priv->state); - bio_free(bio, dst_bio_set); -} - -/* - * Block IO completion. Queue request to be sent back to - * the client (or just confirmation). - */ -static void dst_bio_end_io(struct bio *bio, int err) -{ - struct dst_export_priv *p = bio->bi_private; - struct dst_state *st = p->state; - unsigned long flags; - - spin_lock_irqsave(&st->request_lock, flags); - list_add_tail(&p->request_entry, &st->request_list); - spin_unlock_irqrestore(&st->request_lock, flags); - - wake_up(&st->thread_wait); -} - -/* - * Allocate read request for the server. - */ -static int dst_export_read_request(struct bio *bio, unsigned int total_size) -{ - unsigned int size; - struct page *page; - int err; - - while (total_size) { - err = -ENOMEM; - page = alloc_page(GFP_KERNEL); - if (!page) - goto err_out_exit; - - size = min_t(unsigned int, PAGE_SIZE, total_size); - - err = bio_add_page(bio, page, size, 0); - dprintk("%s: bio: %llu/%u, size: %u, err: %d.\n", - __func__, (u64)bio->bi_sector, bio->bi_size, - size, err); - if (err <= 0) - goto err_out_free_page; - - total_size -= size; - } - - return 0; - -err_out_free_page: - __free_page(page); -err_out_exit: - return err; -} - -/* - * Allocate write request for the server. - * Should not only get pages, but also read data from the network. - */ -static int dst_export_write_request(struct dst_state *st, - struct bio *bio, unsigned int total_size) -{ - unsigned int size; - struct page *page; - void *data; - int err; - - while (total_size) { - err = -ENOMEM; - page = alloc_page(GFP_KERNEL); - if (!page) - goto err_out_exit; - - data = kmap(page); - if (!data) - goto err_out_free_page; - - size = min_t(unsigned int, PAGE_SIZE, total_size); - - err = dst_data_recv(st, data, size); - if (err) - goto err_out_unmap_page; - - err = bio_add_page(bio, page, size, 0); - if (err <= 0) - goto err_out_unmap_page; - - kunmap(page); - - total_size -= size; - } - - return 0; - -err_out_unmap_page: - kunmap(page); -err_out_free_page: - __free_page(page); -err_out_exit: - return err; -} - -/* - * Groovy, we've gotten an IO request from the client. - * Allocate BIO from the bioset, private data from the mempool - * and lots of pages for IO. - */ -int dst_process_io(struct dst_state *st) -{ - struct dst_node *n = st->node; - struct dst_cmd *cmd = st->data; - struct bio *bio; - struct dst_export_priv *priv; - int err = -ENOMEM; - - if (unlikely(!n->bdev)) { - err = -EINVAL; - goto err_out_exit; - } - - bio = bio_alloc_bioset(GFP_KERNEL, - PAGE_ALIGN(cmd->size) >> PAGE_SHIFT, - dst_bio_set); - if (!bio) - goto err_out_exit; - - priv = (struct dst_export_priv *)(((void *)bio) - - sizeof (struct dst_export_priv)); - - priv->state = dst_state_get(st); - priv->bio = bio; - - bio->bi_private = priv; - bio->bi_end_io = dst_bio_end_io; - bio->bi_destructor = dst_bio_destructor; - bio->bi_bdev = n->bdev; - - /* - * Server side is only interested in two low bits: - * uptodate (set by itself actually) and rw block - */ - bio->bi_flags |= cmd->flags & 3; - - bio->bi_rw = cmd->rw; - bio->bi_size = 0; - bio->bi_sector = cmd->sector; - - dst_bio_to_cmd(bio, &priv->cmd, DST_IO_RESPONSE, cmd->id); - - priv->cmd.flags = 0; - priv->cmd.size = cmd->size; - - if (bio_data_dir(bio) == WRITE) { - err = dst_recv_cdata(st, priv->cmd.hash); - if (err) - goto err_out_free; - - err = dst_export_write_request(st, bio, cmd->size); - if (err) - goto err_out_free; - - if (dst_need_crypto(n)) - return dst_export_crypto(n, bio); - } else { - err = dst_export_read_request(bio, cmd->size); - if (err) - goto err_out_free; - } - - dprintk("%s: bio: %llu/%u, rw: %lu, dir: %lu, flags: %lx, phys: %d.\n", - __func__, (u64)bio->bi_sector, bio->bi_size, - bio->bi_rw, bio_data_dir(bio), - bio->bi_flags, bio->bi_phys_segments); - - generic_make_request(bio); - - return 0; - -err_out_free: - bio_put(bio); -err_out_exit: - return err; -} - -/* - * Ok, block IO is ready, let's send it back to the client... - */ -int dst_export_send_bio(struct bio *bio) -{ - struct dst_export_priv *p = bio->bi_private; - struct dst_state *st = p->state; - struct dst_cmd *cmd = &p->cmd; - int err; - - dprintk("%s: id: %llu, bio: %llu/%u, csize: %u, flags: %lu, rw: %lu.\n", - __func__, cmd->id, (u64)bio->bi_sector, bio->bi_size, - cmd->csize, bio->bi_flags, bio->bi_rw); - - dst_convert_cmd(cmd); - - dst_state_lock(st); - if (!st->socket) { - err = -ECONNRESET; - goto err_out_unlock; - } - - if (bio_data_dir(bio) == WRITE) { - /* ... or just confirmation that writing has completed. */ - cmd->size = cmd->csize = 0; - err = dst_data_send_header(st->socket, cmd, - sizeof(struct dst_cmd), 0); - if (err) - goto err_out_unlock; - } else { - err = dst_send_bio(st, cmd, bio); - if (err) - goto err_out_unlock; - } - - dst_state_unlock(st); - - bio_put(bio); - return 0; - -err_out_unlock: - dst_state_unlock(st); - - bio_put(bio); - return err; -} |