diff options
Diffstat (limited to 'drivers/gpu/nvgpu/gk20a/gk20a.c')
-rw-r--r-- | drivers/gpu/nvgpu/gk20a/gk20a.c | 64 |
1 files changed, 63 insertions, 1 deletions
diff --git a/drivers/gpu/nvgpu/gk20a/gk20a.c b/drivers/gpu/nvgpu/gk20a/gk20a.c index 30c0f2fb..1d6fb0e9 100644 --- a/drivers/gpu/nvgpu/gk20a/gk20a.c +++ b/drivers/gpu/nvgpu/gk20a/gk20a.c | |||
@@ -1712,7 +1712,7 @@ static int __exit gk20a_remove(struct platform_device *pdev) | |||
1712 | platform->remove(dev); | 1712 | platform->remove(dev); |
1713 | 1713 | ||
1714 | set_gk20a(pdev, NULL); | 1714 | set_gk20a(pdev, NULL); |
1715 | kfree(g); | 1715 | gk20a_put(g); |
1716 | 1716 | ||
1717 | gk20a_dbg_fn("removed"); | 1717 | gk20a_dbg_fn("removed"); |
1718 | 1718 | ||
@@ -2274,6 +2274,68 @@ int gk20a_read_ptimer(struct gk20a *g, u64 *value) | |||
2274 | return -EBUSY; | 2274 | return -EBUSY; |
2275 | } | 2275 | } |
2276 | 2276 | ||
2277 | /* | ||
2278 | * Free the gk20a struct. | ||
2279 | */ | ||
2280 | static void gk20a_free_cb(struct kref *refcount) | ||
2281 | { | ||
2282 | struct gk20a *g = container_of(refcount, | ||
2283 | struct gk20a, refcount); | ||
2284 | |||
2285 | gk20a_dbg(gpu_dbg_shutdown, "Freeing GK20A struct!"); | ||
2286 | kfree(g); | ||
2287 | } | ||
2288 | |||
2289 | /** | ||
2290 | * gk20a_get() - Increment ref count on driver | ||
2291 | * | ||
2292 | * @g The driver to increment | ||
2293 | * This will fail if the driver is in the process of being released. In that | ||
2294 | * case it will return NULL. Otherwise a pointer to the driver passed in will | ||
2295 | * be returned. | ||
2296 | */ | ||
2297 | struct gk20a * __must_check gk20a_get(struct gk20a *g) | ||
2298 | { | ||
2299 | int success; | ||
2300 | |||
2301 | /* | ||
2302 | * Handle the possibility we are still freeing the gk20a struct while | ||
2303 | * gk20a_get() is called. Unlikely but plausible race condition. Ideally | ||
2304 | * the code will never be in such a situation that this race is | ||
2305 | * possible. | ||
2306 | */ | ||
2307 | success = kref_get_unless_zero(&g->refcount); | ||
2308 | |||
2309 | gk20a_dbg(gpu_dbg_shutdown, "GET: refs currently %d %s", | ||
2310 | atomic_read(&g->refcount.refcount), success ? "" : "(FAILED)"); | ||
2311 | |||
2312 | return success ? g : NULL; | ||
2313 | } | ||
2314 | |||
2315 | /** | ||
2316 | * gk20a_put() - Decrement ref count on driver | ||
2317 | * | ||
2318 | * @g - The driver to decrement | ||
2319 | * | ||
2320 | * Decrement the driver ref-count. If neccesary also free the underlying driver | ||
2321 | * memory | ||
2322 | */ | ||
2323 | void gk20a_put(struct gk20a *g) | ||
2324 | { | ||
2325 | /* | ||
2326 | * Note - this is racy, two instances of this could run before the | ||
2327 | * actual kref_put(0 runs, you could see something like: | ||
2328 | * | ||
2329 | * ... PUT: refs currently 2 | ||
2330 | * ... PUT: refs currently 2 | ||
2331 | * ... Freeing GK20A struct! | ||
2332 | */ | ||
2333 | gk20a_dbg(gpu_dbg_shutdown, "PUT: refs currently %d", | ||
2334 | atomic_read(&g->refcount.refcount)); | ||
2335 | |||
2336 | kref_put(&g->refcount, gk20a_free_cb); | ||
2337 | } | ||
2338 | |||
2277 | MODULE_LICENSE("GPL v2"); | 2339 | MODULE_LICENSE("GPL v2"); |
2278 | module_init(gk20a_init); | 2340 | module_init(gk20a_init); |
2279 | module_exit(gk20a_exit); | 2341 | module_exit(gk20a_exit); |