diff options
-rw-r--r-- | drivers/char/ipmi/ipmi_msghandler.c | 205 |
1 files changed, 190 insertions, 15 deletions
diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index f42459a27b19..9157a9e17c36 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c | |||
@@ -159,6 +159,9 @@ static struct proc_dir_entry *proc_ipmi_root; | |||
159 | */ | 159 | */ |
160 | #define IPMI_REQUEST_EV_TIME (1000 / (IPMI_TIMEOUT_TIME)) | 160 | #define IPMI_REQUEST_EV_TIME (1000 / (IPMI_TIMEOUT_TIME)) |
161 | 161 | ||
162 | /* How long should we cache dynamic device IDs? */ | ||
163 | #define IPMI_DYN_DEV_ID_EXPIRY (10 * HZ) | ||
164 | |||
162 | /* | 165 | /* |
163 | * The main "user" data structure. | 166 | * The main "user" data structure. |
164 | */ | 167 | */ |
@@ -264,8 +267,12 @@ struct ipmi_proc_entry { | |||
264 | 267 | ||
265 | struct bmc_device { | 268 | struct bmc_device { |
266 | struct platform_device pdev; | 269 | struct platform_device pdev; |
267 | struct ipmi_device_id id; | ||
268 | struct list_head intfs; | 270 | struct list_head intfs; |
271 | struct ipmi_device_id id; | ||
272 | struct ipmi_device_id fetch_id; | ||
273 | int dyn_id_set; | ||
274 | unsigned long dyn_id_expiry; | ||
275 | struct mutex dyn_mutex; /* protects id & dyn* fields */ | ||
269 | unsigned char guid[16]; | 276 | unsigned char guid[16]; |
270 | int guid_set; | 277 | int guid_set; |
271 | struct kref usecount; | 278 | struct kref usecount; |
@@ -402,6 +409,13 @@ struct ipmi_smi { | |||
402 | /* Used for wake ups at startup. */ | 409 | /* Used for wake ups at startup. */ |
403 | wait_queue_head_t waitq; | 410 | wait_queue_head_t waitq; |
404 | 411 | ||
412 | /* | ||
413 | * Prevents the interface from being unregistered when the | ||
414 | * interface is used by being looked up through the BMC | ||
415 | * structure. | ||
416 | */ | ||
417 | struct mutex bmc_reg_mutex; | ||
418 | |||
405 | struct bmc_device *bmc; | 419 | struct bmc_device *bmc; |
406 | bool bmc_registered; | 420 | bool bmc_registered; |
407 | struct list_head bmc_link; | 421 | struct list_head bmc_link; |
@@ -491,6 +505,11 @@ struct ipmi_smi { | |||
491 | * interface comes in with a NULL user, call this routine with | 505 | * interface comes in with a NULL user, call this routine with |
492 | * it. Note that the message will still be freed by the | 506 | * it. Note that the message will still be freed by the |
493 | * caller. This only works on the system interface. | 507 | * caller. This only works on the system interface. |
508 | * | ||
509 | * The only user outside of initialization an panic handling is | ||
510 | * the dynamic device id fetching, so no mutex is currently | ||
511 | * required on this. If more users come along, some sort of | ||
512 | * mutex will be required. | ||
494 | */ | 513 | */ |
495 | void (*null_user_handler)(ipmi_smi_t intf, struct ipmi_recv_msg *msg); | 514 | void (*null_user_handler)(ipmi_smi_t intf, struct ipmi_recv_msg *msg); |
496 | 515 | ||
@@ -2081,14 +2100,158 @@ int ipmi_request_supply_msgs(ipmi_user_t user, | |||
2081 | } | 2100 | } |
2082 | EXPORT_SYMBOL(ipmi_request_supply_msgs); | 2101 | EXPORT_SYMBOL(ipmi_request_supply_msgs); |
2083 | 2102 | ||
2103 | static void bmc_device_id_handler(ipmi_smi_t intf, struct ipmi_recv_msg *msg) | ||
2104 | { | ||
2105 | int rv; | ||
2106 | |||
2107 | if ((msg->addr.addr_type != IPMI_SYSTEM_INTERFACE_ADDR_TYPE) | ||
2108 | || (msg->msg.netfn != IPMI_NETFN_APP_RESPONSE) | ||
2109 | || (msg->msg.cmd != IPMI_GET_DEVICE_ID_CMD)) { | ||
2110 | pr_warn(PFX "invalid device_id msg: addr_type=%d netfn=%x cmd=%x\n", | ||
2111 | msg->addr.addr_type, msg->msg.netfn, msg->msg.cmd); | ||
2112 | return; | ||
2113 | } | ||
2114 | |||
2115 | rv = ipmi_demangle_device_id(msg->msg.netfn, msg->msg.cmd, | ||
2116 | msg->msg.data, msg->msg.data_len, &intf->bmc->fetch_id); | ||
2117 | if (rv) { | ||
2118 | pr_warn(PFX "device id demangle failed: %d\n", rv); | ||
2119 | intf->bmc->dyn_id_set = 0; | ||
2120 | } else { | ||
2121 | /* | ||
2122 | * Make sure the id data is available before setting | ||
2123 | * dyn_id_set. | ||
2124 | */ | ||
2125 | smp_wmb(); | ||
2126 | intf->bmc->dyn_id_set = 1; | ||
2127 | } | ||
2128 | |||
2129 | wake_up(&intf->waitq); | ||
2130 | } | ||
2131 | |||
2132 | static int | ||
2133 | send_get_device_id_cmd(ipmi_smi_t intf) | ||
2134 | { | ||
2135 | struct ipmi_system_interface_addr si; | ||
2136 | struct kernel_ipmi_msg msg; | ||
2137 | |||
2138 | si.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; | ||
2139 | si.channel = IPMI_BMC_CHANNEL; | ||
2140 | si.lun = 0; | ||
2141 | |||
2142 | msg.netfn = IPMI_NETFN_APP_REQUEST; | ||
2143 | msg.cmd = IPMI_GET_DEVICE_ID_CMD; | ||
2144 | msg.data = NULL; | ||
2145 | msg.data_len = 0; | ||
2146 | |||
2147 | return i_ipmi_request(NULL, | ||
2148 | intf, | ||
2149 | (struct ipmi_addr *) &si, | ||
2150 | 0, | ||
2151 | &msg, | ||
2152 | intf, | ||
2153 | NULL, | ||
2154 | NULL, | ||
2155 | 0, | ||
2156 | intf->channels[0].address, | ||
2157 | intf->channels[0].lun, | ||
2158 | -1, 0); | ||
2159 | } | ||
2160 | |||
2161 | static int __get_device_id(ipmi_smi_t intf, struct bmc_device *bmc) | ||
2162 | { | ||
2163 | int rv; | ||
2164 | |||
2165 | bmc->dyn_id_set = 2; | ||
2166 | |||
2167 | intf->null_user_handler = bmc_device_id_handler; | ||
2168 | |||
2169 | rv = send_get_device_id_cmd(intf); | ||
2170 | if (rv) | ||
2171 | return rv; | ||
2172 | |||
2173 | wait_event(intf->waitq, bmc->dyn_id_set != 2); | ||
2174 | |||
2175 | if (!bmc->dyn_id_set) | ||
2176 | rv = -EIO; /* Something went wrong in the fetch. */ | ||
2177 | |||
2178 | /* dyn_id_set makes the id data available. */ | ||
2179 | smp_rmb(); | ||
2180 | |||
2181 | intf->null_user_handler = NULL; | ||
2182 | |||
2183 | return rv; | ||
2184 | } | ||
2185 | |||
2186 | /* | ||
2187 | * Fetch the device id for the bmc/interface. You must pass in either | ||
2188 | * bmc or intf, this code will get the other one. If the data has | ||
2189 | * been recently fetched, this will just use the cached data. Otherwise | ||
2190 | * it will run a new fetch. | ||
2191 | * | ||
2192 | * Except for the first time this is called (in ipmi_register_smi()), | ||
2193 | * this will always return good data; | ||
2194 | */ | ||
2084 | static int bmc_get_device_id(ipmi_smi_t intf, struct bmc_device *bmc, | 2195 | static int bmc_get_device_id(ipmi_smi_t intf, struct bmc_device *bmc, |
2085 | struct ipmi_device_id *id) | 2196 | struct ipmi_device_id *id) |
2086 | { | 2197 | { |
2087 | if (!bmc) | 2198 | int rv = 0; |
2199 | int prev_dyn_id_set; | ||
2200 | |||
2201 | if (!intf) { | ||
2202 | mutex_lock(&bmc->dyn_mutex); | ||
2203 | retry_bmc_lock: | ||
2204 | if (list_empty(&bmc->intfs)) { | ||
2205 | mutex_unlock(&bmc->dyn_mutex); | ||
2206 | return -ENOENT; | ||
2207 | } | ||
2208 | intf = list_first_entry(&bmc->intfs, struct ipmi_smi, | ||
2209 | bmc_link); | ||
2210 | kref_get(&intf->refcount); | ||
2211 | mutex_unlock(&bmc->dyn_mutex); | ||
2212 | mutex_lock(&intf->bmc_reg_mutex); | ||
2213 | mutex_lock(&bmc->dyn_mutex); | ||
2214 | if (intf != list_first_entry(&bmc->intfs, struct ipmi_smi, | ||
2215 | bmc_link)) { | ||
2216 | mutex_unlock(&intf->bmc_reg_mutex); | ||
2217 | kref_put(&intf->refcount, intf_free); | ||
2218 | goto retry_bmc_lock; | ||
2219 | } | ||
2220 | } else { | ||
2221 | mutex_lock(&intf->bmc_reg_mutex); | ||
2088 | bmc = intf->bmc; | 2222 | bmc = intf->bmc; |
2223 | mutex_lock(&bmc->dyn_mutex); | ||
2224 | kref_get(&intf->refcount); | ||
2225 | } | ||
2089 | 2226 | ||
2090 | *id = bmc->id; | 2227 | /* If we have a valid and current ID, just return that. */ |
2091 | return 0; | 2228 | if (bmc->dyn_id_set && time_is_after_jiffies(bmc->dyn_id_expiry)) |
2229 | goto out; | ||
2230 | |||
2231 | prev_dyn_id_set = bmc->dyn_id_set; | ||
2232 | |||
2233 | rv = __get_device_id(intf, bmc); | ||
2234 | if (rv) | ||
2235 | goto out; | ||
2236 | |||
2237 | memcpy(&bmc->id, &bmc->fetch_id, sizeof(bmc->id)); | ||
2238 | |||
2239 | bmc->dyn_id_expiry = jiffies + IPMI_DYN_DEV_ID_EXPIRY; | ||
2240 | |||
2241 | out: | ||
2242 | if (rv && prev_dyn_id_set) { | ||
2243 | rv = 0; /* Ignore failures if we have previous data. */ | ||
2244 | bmc->dyn_id_set = prev_dyn_id_set; | ||
2245 | } | ||
2246 | |||
2247 | if (id) | ||
2248 | *id = bmc->id; | ||
2249 | |||
2250 | mutex_unlock(&bmc->dyn_mutex); | ||
2251 | mutex_unlock(&intf->bmc_reg_mutex); | ||
2252 | |||
2253 | kref_put(&intf->refcount, intf_free); | ||
2254 | return rv; | ||
2092 | } | 2255 | } |
2093 | 2256 | ||
2094 | #ifdef CONFIG_PROC_FS | 2257 | #ifdef CONFIG_PROC_FS |
@@ -2615,17 +2778,23 @@ static void ipmi_bmc_unregister(ipmi_smi_t intf) | |||
2615 | if (!intf->bmc_registered) | 2778 | if (!intf->bmc_registered) |
2616 | return; | 2779 | return; |
2617 | 2780 | ||
2781 | mutex_lock(&intf->bmc_reg_mutex); | ||
2782 | |||
2618 | sysfs_remove_link(&intf->si_dev->kobj, "bmc"); | 2783 | sysfs_remove_link(&intf->si_dev->kobj, "bmc"); |
2619 | sysfs_remove_link(&bmc->pdev.dev.kobj, intf->my_dev_name); | 2784 | sysfs_remove_link(&bmc->pdev.dev.kobj, intf->my_dev_name); |
2620 | kfree(intf->my_dev_name); | 2785 | kfree(intf->my_dev_name); |
2621 | intf->my_dev_name = NULL; | 2786 | intf->my_dev_name = NULL; |
2622 | 2787 | ||
2623 | mutex_lock(&ipmidriver_mutex); | 2788 | mutex_lock(&bmc->dyn_mutex); |
2624 | list_del(&intf->bmc_link); | 2789 | list_del(&intf->bmc_link); |
2790 | mutex_unlock(&bmc->dyn_mutex); | ||
2625 | intf->bmc = NULL; | 2791 | intf->bmc = NULL; |
2792 | mutex_lock(&ipmidriver_mutex); | ||
2626 | kref_put(&bmc->usecount, cleanup_bmc_device); | 2793 | kref_put(&bmc->usecount, cleanup_bmc_device); |
2627 | mutex_unlock(&ipmidriver_mutex); | 2794 | mutex_unlock(&ipmidriver_mutex); |
2628 | intf->bmc_registered = false; | 2795 | intf->bmc_registered = false; |
2796 | |||
2797 | mutex_unlock(&intf->bmc_reg_mutex); | ||
2629 | } | 2798 | } |
2630 | 2799 | ||
2631 | static int ipmi_bmc_register(ipmi_smi_t intf, int ifnum) | 2800 | static int ipmi_bmc_register(ipmi_smi_t intf, int ifnum) |
@@ -2653,11 +2822,11 @@ static int ipmi_bmc_register(ipmi_smi_t intf, int ifnum) | |||
2653 | */ | 2822 | */ |
2654 | if (old_bmc) { | 2823 | if (old_bmc) { |
2655 | kfree(bmc); | 2824 | kfree(bmc); |
2656 | mutex_lock(&ipmidriver_mutex); | 2825 | bmc = old_bmc; |
2657 | intf->bmc = old_bmc; | 2826 | intf->bmc = old_bmc; |
2827 | mutex_lock(&bmc->dyn_mutex); | ||
2658 | list_add_tail(&intf->bmc_link, &bmc->intfs); | 2828 | list_add_tail(&intf->bmc_link, &bmc->intfs); |
2659 | mutex_unlock(&ipmidriver_mutex); | 2829 | mutex_unlock(&bmc->dyn_mutex); |
2660 | bmc = old_bmc; | ||
2661 | 2830 | ||
2662 | printk(KERN_INFO | 2831 | printk(KERN_INFO |
2663 | "ipmi: interfacing existing BMC (man_id: 0x%6.6x," | 2832 | "ipmi: interfacing existing BMC (man_id: 0x%6.6x," |
@@ -2677,10 +2846,12 @@ static int ipmi_bmc_register(ipmi_smi_t intf, int ifnum) | |||
2677 | bmc->pdev.dev.type = &bmc_device_type; | 2846 | bmc->pdev.dev.type = &bmc_device_type; |
2678 | kref_init(&bmc->usecount); | 2847 | kref_init(&bmc->usecount); |
2679 | 2848 | ||
2680 | rv = platform_device_register(&bmc->pdev); | 2849 | intf->bmc = bmc; |
2681 | mutex_lock(&ipmidriver_mutex); | 2850 | mutex_lock(&bmc->dyn_mutex); |
2682 | list_add_tail(&intf->bmc_link, &bmc->intfs); | 2851 | list_add_tail(&intf->bmc_link, &bmc->intfs); |
2683 | mutex_unlock(&ipmidriver_mutex); | 2852 | mutex_unlock(&bmc->dyn_mutex); |
2853 | |||
2854 | rv = platform_device_register(&bmc->pdev); | ||
2684 | if (rv) { | 2855 | if (rv) { |
2685 | printk(KERN_ERR | 2856 | printk(KERN_ERR |
2686 | "ipmi_msghandler:" | 2857 | "ipmi_msghandler:" |
@@ -2743,18 +2914,20 @@ out_unlink1: | |||
2743 | sysfs_remove_link(&intf->si_dev->kobj, "bmc"); | 2914 | sysfs_remove_link(&intf->si_dev->kobj, "bmc"); |
2744 | 2915 | ||
2745 | out_put_bmc: | 2916 | out_put_bmc: |
2746 | mutex_lock(&ipmidriver_mutex); | 2917 | mutex_lock(&bmc->dyn_mutex); |
2747 | list_del(&intf->bmc_link); | 2918 | list_del(&intf->bmc_link); |
2919 | mutex_unlock(&bmc->dyn_mutex); | ||
2748 | intf->bmc = NULL; | 2920 | intf->bmc = NULL; |
2921 | mutex_lock(&ipmidriver_mutex); | ||
2749 | kref_put(&bmc->usecount, cleanup_bmc_device); | 2922 | kref_put(&bmc->usecount, cleanup_bmc_device); |
2750 | mutex_unlock(&ipmidriver_mutex); | 2923 | mutex_unlock(&ipmidriver_mutex); |
2751 | goto out; | 2924 | goto out; |
2752 | 2925 | ||
2753 | out_list_del: | 2926 | out_list_del: |
2754 | mutex_lock(&ipmidriver_mutex); | 2927 | mutex_lock(&bmc->dyn_mutex); |
2755 | list_del(&intf->bmc_link); | 2928 | list_del(&intf->bmc_link); |
2929 | mutex_unlock(&bmc->dyn_mutex); | ||
2756 | intf->bmc = NULL; | 2930 | intf->bmc = NULL; |
2757 | mutex_unlock(&ipmidriver_mutex); | ||
2758 | put_device(&bmc->pdev.dev); | 2931 | put_device(&bmc->pdev.dev); |
2759 | goto out; | 2932 | goto out; |
2760 | } | 2933 | } |
@@ -2976,9 +3149,11 @@ int ipmi_register_smi(const struct ipmi_smi_handlers *handlers, | |||
2976 | return -ENOMEM; | 3149 | return -ENOMEM; |
2977 | } | 3150 | } |
2978 | INIT_LIST_HEAD(&intf->bmc->intfs); | 3151 | INIT_LIST_HEAD(&intf->bmc->intfs); |
3152 | mutex_init(&intf->bmc->dyn_mutex); | ||
3153 | INIT_LIST_HEAD(&intf->bmc_link); | ||
3154 | mutex_init(&intf->bmc_reg_mutex); | ||
2979 | intf->intf_num = -1; /* Mark it invalid for now. */ | 3155 | intf->intf_num = -1; /* Mark it invalid for now. */ |
2980 | kref_init(&intf->refcount); | 3156 | kref_init(&intf->refcount); |
2981 | intf->bmc->id = *device_id; | ||
2982 | intf->si_dev = si_dev; | 3157 | intf->si_dev = si_dev; |
2983 | for (j = 0; j < IPMI_MAX_CHANNELS; j++) { | 3158 | for (j = 0; j < IPMI_MAX_CHANNELS; j++) { |
2984 | intf->channels[j].address = IPMI_BMC_SLAVE_ADDR; | 3159 | intf->channels[j].address = IPMI_BMC_SLAVE_ADDR; |