Update to 3.4-final.
[linux-flexiantxendom0-3.2.10.git] / drivers / xen / scsiback / translate.c
1 /*
2  * Xen SCSI backend driver
3  *
4  * Copyright (c) 2008, FUJITSU Limited
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License version 2
8  * as published by the Free Software Foundation; or, when distributed
9  * separately from the Linux kernel or incorporated into other
10  * software packages, subject to the following license:
11  * 
12  * Permission is hereby granted, free of charge, to any person obtaining a copy
13  * of this source file (the "Software"), to deal in the Software without
14  * restriction, including without limitation the rights to use, copy, modify,
15  * merge, publish, distribute, sublicense, and/or sell copies of the Software,
16  * and to permit persons to whom the Software is furnished to do so, subject to
17  * the following conditions:
18  * 
19  * The above copyright notice and this permission notice shall be included in
20  * all copies or substantial portions of the Software.
21  * 
22  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
28  * IN THE SOFTWARE.
29  */
30
31 #include <linux/list.h>
32 #include <linux/gfp.h>
33
34 #include "common.h"
35
36 /*
37   Initialize the translation entry list
38 */
39 void scsiback_init_translation_table(struct vscsibk_info *info)
40 {
41         INIT_LIST_HEAD(&info->v2p_entry_lists);
42         spin_lock_init(&info->v2p_lock);
43 }
44
45
46 /*
47   Add a new translation entry
48 */
49 int scsiback_add_translation_entry(struct vscsibk_info *info,
50                         struct scsi_device *sdev, struct ids_tuple *v)
51 {
52         int err = 0;
53         struct v2p_entry *entry;
54         struct v2p_entry *new;
55         struct list_head *head = &(info->v2p_entry_lists);
56         unsigned long flags;
57         
58         spin_lock_irqsave(&info->v2p_lock, flags);
59
60         /* Check double assignment to identical virtual ID */
61         list_for_each_entry(entry, head, l) {
62                 if ((entry->v.chn == v->chn) &&
63                     (entry->v.tgt == v->tgt) &&
64                     (entry->v.lun == v->lun)) {
65                         pr_warning("scsiback: Virtual ID is already used. "
66                                    "Assignment was not performed.\n");
67                         err = -EEXIST;
68                         goto out;
69                 }
70
71         }
72
73         /* Create a new translation entry and add to the list */
74         if ((new = kmalloc(sizeof(struct v2p_entry), GFP_ATOMIC)) == NULL) {
75                 pr_err("scsiback: %s: kmalloc() error\n", __FUNCTION__);
76                 err = -ENOMEM;
77                 goto out;
78         }
79         new->v = *v;
80         new->sdev = sdev;
81         list_add_tail(&new->l, head);
82
83 out:    
84         spin_unlock_irqrestore(&info->v2p_lock, flags);
85         return err;
86 }
87
88
89 /*
90   Delete the translation entry specfied
91 */
92 int scsiback_del_translation_entry(struct vscsibk_info *info,
93                                 struct ids_tuple *v)
94 {
95         struct v2p_entry *entry;
96         struct list_head *head = &(info->v2p_entry_lists);
97         unsigned long flags;
98
99         spin_lock_irqsave(&info->v2p_lock, flags);
100         /* Find out the translation entry specified */
101         list_for_each_entry(entry, head, l) {
102                 if ((entry->v.chn == v->chn) &&
103                     (entry->v.tgt == v->tgt) &&
104                     (entry->v.lun == v->lun)) {
105                         goto found;
106                 }
107         }
108
109         spin_unlock_irqrestore(&info->v2p_lock, flags);
110         return 1;
111
112 found:
113         /* Delete the translation entry specfied */
114         scsi_device_put(entry->sdev);
115         list_del(&entry->l);
116         kfree(entry);
117
118         spin_unlock_irqrestore(&info->v2p_lock, flags);
119         return 0;
120 }
121
122
123 /*
124   Perform virtual to physical translation
125 */
126 struct scsi_device *scsiback_do_translation(struct vscsibk_info *info,
127                         struct ids_tuple *v)
128 {
129         struct v2p_entry *entry;
130         struct list_head *head = &(info->v2p_entry_lists);
131         struct scsi_device *sdev = NULL;
132         unsigned long flags;
133
134         spin_lock_irqsave(&info->v2p_lock, flags);
135         list_for_each_entry(entry, head, l) {
136                 if ((entry->v.chn == v->chn) &&
137                     (entry->v.tgt == v->tgt) &&
138                     (entry->v.lun == v->lun)) {
139                         sdev = entry->sdev;
140                         goto out;
141                 }
142         }
143 out:
144         spin_unlock_irqrestore(&info->v2p_lock, flags);
145         return sdev;
146 }
147
148
149 /*
150   Release the translation entry specfied
151 */
152 void scsiback_release_translation_entry(struct vscsibk_info *info)
153 {
154         struct v2p_entry *entry, *tmp;
155         struct list_head *head = &(info->v2p_entry_lists);
156         unsigned long flags;
157
158         spin_lock_irqsave(&info->v2p_lock, flags);
159         list_for_each_entry_safe(entry, tmp, head, l) {
160                 scsi_device_put(entry->sdev);
161                 list_del(&entry->l);
162                 kfree(entry);
163         }
164
165         spin_unlock_irqrestore(&info->v2p_lock, flags);
166         return;
167
168 }